【问题标题】:Question about deleting a pointer. Which class should it be deleted from?关于删除指针的问题。应该从哪个类中删除?
【发布时间】:2012-01-06 09:26:45
【问题描述】:

我有两个四类:

  • MainClass(事情开始的类)
  • XmlReader(用于解析 xml 文件的类)
  • SerialPortSettings(保存有关从 xml 文件中读取的串行端口的信息,例如波特率、通信端口等)
  • SerialPortListener(在其构造函数中引用一个 SerialPortSettings 对象)

MainClass 有一种从 xml 文件中读取内容的方法。 在这个方法中,它首先创建一个 XmlReader 的实例,并给它一个 xml 文件作为构造函数参数。这个 xmlReader 只需要存在于这个方法中:

XmlReader xmlReader (xmlFile);

xmlReader 解析 xmlFile。 MainClass 通过调用 XmlReader 中的 get-methods 来访问 xml-stuff。到目前为止一切都很好。

然而,XmlReader 提供的方法之一是根据从 xml 文件中读取的信息创建 SerialPortSettings 类型的对象:

SerialPortSettings* XmlReader::getSerialPortSettings() {
  .... // reading stuff from xml file
  return new SerialPortSettings(baudRate, dataBits, comport);
}

MainClass调用该方法,返回值存储在指针中:

SerialPortSettings* settings = xmlReader.getSerialPortSettings();

MainClass 做的下一件事是创建一个 SerialPortListener(这是一个成员变量,在 MainClass 退出之前必须存在)。 SerialPortListener 在其构造函数中引用了 SerialPortSettings:

m_serialPortListener = new SerialPortListener(*settings);

因此,在 MainClass 退出之前,SerialPortSettings 也必须存在,因此我将其创建为指针。

这就是线索:

SerialPortListener 析构函数中,我尝试删除 SerialPortSettings-对象:

SerialPortListener::~SerialPortListener() {
  delete &m_settings;
}

然后在 MainClass 析构函数中,我删除了 SerialPortListener-object:

MainClass::~MainClass() {
  delete m_serialPortListener;
} 

这失败了。我收到一条错误消息,说我在主类中删除了两次:

*** glibc detected *** ./ioserver: double free or corruption (out): 0x00860d80 ***

当我从 SerialPortListener 中删除删除 &m_settings 时,它工作正常。 但是什么时候应该删除指针呢?正确的做法是什么?我真的希望我的 xml 阅读器创建 SerialPortSettings - 对象,将所有信息(波特率、comport 等)返回到 MainClass 并创建 SerialPortSettings 对象本身。

【问题讨论】:

    标签: c++ pointers


    【解决方案1】:

    一个好的解决方案是让xmlReader::getSerialPortSettings 按值返回SerialPortSettings

    让编译器做优化。

    但如果您确实需要处理指针生命周期,请使用智能指针,例如std::auto_ptrboost::shared_ptr。关键思想是定义所有权。所有者(在boost::shared_ptr 的情况下是引用该对象的智能指针的集合)负责删除——没有其他人。

    干杯,

    【讨论】:

      【解决方案2】:

      应该在 MainClass 的末尾删除指针。

      【讨论】:

        【解决方案3】:

        在引用上使用delete 毫无意义(至少对我而言)。

        不让 XML 阅读器创建新对象会更简洁;将 SerialPortSettings 视为“哑”容器,只需传入一个引用以填充 XML 中的数据:

        XmlReader::getSerialPortSettings(SerialPortSettings& settings);
        

        然后,实际实例可以是主程序中的局部变量,并在创建时(通过 const 引用,这次)传递给串行端口:

        SerialPortSettings portSettings;
        m_xmlReader->getSerialPortSettings(portSettings);
        m_serialPort = new SerialPort(portSettings);
        

        设置实例的生命周期自然与它所在的范围相同,因为它只是一个局部变量。

        如果读取XML的主类中的方法需要在串口超出范围之前退出,则可以将设置设为主类的成员变量。

        【讨论】:

        • 我喜欢这个解决方案!谢谢:)
        【解决方案4】:

        m_settings 的数据类型是什么?它是 SerialPortSettings* 还是 SerialPortSettings?如果是后者,您无论如何都不能像那样删除它,因为它是在堆栈上分配的。如果是前者(指针),则不需要引用运算符。直接写delete m_settings;

        【讨论】:

        • 数据类型 og m_settings 不是指针(无星号)。在 serialportlistener.h 我这样定义它: SerialPortSettings m_settings;
        • 刚刚注意到这一行:m_serialPortListener = new SerialPortListener(*settings);您应该删除设置;在此行之后,假设您在 SerialPortListener() 中复制其内容。
        【解决方案5】:

        删除中的一个简单错字:

        delete &m_settings;
        

        应该是:

        delete m_settings;
        

        对于任何指针,您应该决定谁owns 指针,应该是谁删除它。

        或者您可以使用诸如shared_ptr 之类的智能指针并完全消除问题。

        【讨论】:

          【解决方案6】:
          SerialPortListener::~SerialPortListener() {
            delete &m_settings;
          }
          

          那个方块看起来很奇怪。您确定您没有尝试通过引用删除值吗?因为 C++ 在你删除类时会自动执行它,所以你的删除实际上是在尝试删除两次。

          【讨论】:

            【解决方案7】:

            好的,首先,您错过了真正重要的信息,即 SerialPortListener::m_settings 是如何存储的。由于您遇到的错误,我猜您实际上是在存储它的副本,这意味着:我打赌您有这样的东西:

            class SerialPortListener {
                SerialPortSettings m_settings;
            
                SerialPortListener(SerialPortSettings set) {
                    m_settings = set;
                }
            }
            

            如果它与此类似,那么侦听器会将对象的副本保存在它自己的内存中,并且删除它没有意义,因为它不是指针。经验法则,在您知道自己在做什么并意识到自己真正需要之前,永远不要删除 &anything。

            就“正确性”而言,指针应该由主类释放,因为它是创建它的人。或者,如果您在主类中没有任何用途,并希望侦听器删除它,请在侦听器中保存指针而不是对象或引用。

            【讨论】:

            • 几乎你写的,除了 SerialPortListener 的构造函数有一个 SerialPortSettings 的参考参数: SerialPortListener(SerialPortSettings& set) { m_settings = set; }
            • 好吧,重要的是无论如何 m_settings 声明。 I 会做的是声明它一个指针并在你正在做的地方删除它,无论如何
            【解决方案8】:

            我最终使 m_serialPortSettings 成为 SerialPortListener 中的指针,并从那里删除它。

            【讨论】:

              猜你喜欢
              • 2011-01-23
              • 2012-10-26
              • 1970-01-01
              • 1970-01-01
              • 2016-07-07
              • 2011-03-13
              • 2022-09-30
              • 2013-11-15
              • 1970-01-01
              相关资源
              最近更新 更多