【问题标题】:std::vector of object containing auto_ptr behaves strangely包含 auto_ptr 的对象的 std::vector 行为异常
【发布时间】:2012-10-17 07:46:38
【问题描述】:

我想使用 Qt 库为我的应用程序创建一个控制面板,为此我创建了类 Controls

Controls:创建一个滑块并旋转和一个标签,并将它们组织成水平布局。

但是当我想创建std::vector<Controls> 时,程序运行没有错误,但现在控件正在创建!那么为什么没有控件。

控件.h

class Controls 
{

private:

    QHBoxLayout Layout ;
    string Controlname;
    std::auto_ptr<QLabel> Label ;
    std::auto_ptr<QSlider> Slider ;
    std::auto_ptr<QSpinBox> Spin ;

public:
    Controls();
    Controls(QLayout &Parent , string name , const int &Default_value);
    Controls(const Controls &copy);
    ~Controls();

    QLabel *const Get_Label()const { return Label.get() ; }
    QSlider *const Get_Slider()const { return Slider.get() ; }
    QSpinBox *const Get_Spin()const { return Spin.get() ; }
    QHBoxLayout *const Get_Layout() {return &Layout;}

    void SetValue(const int &newvalue);

    Controls &operator= (const Controls &copy);


};

Controls.cpp

Controls &Controls::operator= (const Controls &copy)
{

Label = std::auto_ptr<QLabel> ( new QLabel() ) ;
Slider = std::auto_ptr<QSlider> ( new QSlider() ) ;
Spin = std::auto_ptr<QSpinBox> ( new QSpinBox() ) ;

    this->Controlname = copy.Controlname ;
    this->Slider.get()->setValue( copy.Slider.get()->value() );
    this->Spin.get()->setValue( copy.Spin.get()->value() );

    return *this ;
}
Controls::Controls(const Controls &copy)
{
    this->Controlname = copy.Controlname ;
    this->Slider.get()->setValue( copy.Slider.get()->value() );
    this->Spin.get()->setValue( copy.Spin.get()->value() );

}
Controls::Controls()
{

    Label = std::auto_ptr<QLabel> ( new QLabel() ) ;
    Slider = std::auto_ptr<QSlider> ( new QSlider() ) ;
    Spin = std::auto_ptr<QSpinBox> ( new QSpinBox() ) ;

    Slider->setValue(0);
    Slider->setOrientation(Qt::Horizontal);
    Label->setText(QString ("unamed"));
    Spin->setValue(0);


    Layout.addWidget(Label.get() , 0 , 0);
    Layout.addWidget(Slider.get() , 0 , 0);
    Layout.addWidget(Spin.get() , 0 , 0);

    QObject::connect(Slider.get() , SIGNAL(valueChanged(int) ) , Spin.get() , SLOT(setValue(int)));
    QObject::connect(Spin.get() , SIGNAL(valueChanged(int) ) , Slider.get() , SLOT(setValue(int)));

}
Controls::Controls(QLayout &Parent , string name , const int &Default_value)
{
    Controlname = name ;

    Label = std::auto_ptr<QLabel> ( new QLabel() ) ;
    Slider = std::auto_ptr<QSlider> ( new QSlider() ) ;
    Spin = std::auto_ptr<QSpinBox> ( new QSpinBox() ) ;

    Slider->setValue(Default_value*100);
    Slider->setOrientation(Qt::Horizontal);
    Label->setText(QString (name.c_str()));
    Spin->setValue(Default_value*100);


    Layout.addWidget(Label.get() , 0 , 0);
    Layout.addWidget(Slider.get() , 0 , 0);
    Layout.addWidget(Spin.get() , 0 , 0);

    QObject::connect(Slider.get() , SIGNAL(valueChanged(int) ) , Spin.get() , SLOT(setValue(int)));
    QObject::connect(Spin.get() , SIGNAL(valueChanged(int) ) , Slider.get() , SLOT(setValue(int)));

    Parent.addItem(&Layout);

}

void Controls::SetValue(const int &newvalue)
{
    Slider.get()->setValue(newvalue);
}
Controls::~Controls()
{

}

main.cpp

int main(int argc, char *argv[])
{


    QApplication app (argc , argv );


    QVBoxLayout layout ;
    auto_ptr<QWidget> Panel = auto_ptr<QWidget> (new QWidget()) ;

    vector<Controls> g ;
    g.push_back(Controls(layout , "WHITE_BALANCE_RED_V" , 56 ));

        Panel.get()->setLayout(&layout);
    Panel.get()->show();

        return app.exec();

}

编辑:

我从 ~controls 中删除了 auto_ptr 析构函数,当我再次运行它时,我得到了这个异常

pure virtual method called
terminate called without an active exception
  • 我初始化了滑块并在复制构造函数中旋转

【问题讨论】:

  • 不用多说,很明显这个程序有未定义的行为,因为(在~Controls())你显式调用了auto_ptr成员的析构函数,然后它们被隐式销毁为正常销毁Controls的一部分。
  • 如果你使用auto_ptr,不要在里面放析构函数。 Rule of Zero.
  • 谢谢你!我删除了 auot_ptr 析构函数,我的程序抛出了这个名为 terminate 的纯虚拟方法,在没有活动异常的情况下调用了
  • 这是因为 Controls::copy_ctor 不正确。您可以尝试只创建 Controls 对象而不将其放入向量中,然后查看您的程序是否有效。它会在退出尝试双重删除 QVBoxLayout 时崩溃,但这是另一回事。

标签: c++ qt vector auto-ptr


【解决方案1】:

这里有两个“奇怪”的东西,都与auto_ptr有关。

第一个是成员析构函数在嵌入析构函数退出后自动调用。因此显式销毁成员变量会导致“双重销毁”,这是编译器无法管理的。

(注意:变量被称为“指针”这一事实不会使它不再是一个变量:如果使用原始指针,并且调用 delete pointer 不会破坏指针-r ,但是指针-d,auto_ptr 自己做的事情)。

第二个是auto_ptr 不可复制:实际上它使用复制运算符来实现移动语义,但是...容器(如std::vector)假定复制是...复制(不可移动)。

在大多数std::vector(以及std::list)实现中,这不是问题,因为实现者关注这一点并避免他们的容器同时生成存在的副本。但是一些突变算法不能始终如一地工作,仅仅是因为它们无意中“移动”到了错误的地方——在函数返回后就会被销毁的地方......叹息!)

第二个方面由 C++11 解决,通过实现支持可复制和可移动元素的容器(作为配备 r 值引用的 C++11,复制和移动是很好区分的操作)并弃用 auto_ptr in赞成unique_ptr,即实施“移动”不是“高于复制”而是“而不是复制”。

故事的寓意:在 C++11 中使用 unique_ptr 而不是 auto_ptr

在 C++03 中,不要在容器或必须留在容器中的对象中使用 auto_ptr

使用原始指针并定义适当的(为您的对象)复制语义(通过执行“深度复制”使指针的副本指向对象的副本)或共享语义(通过使指针指向相同的对象,并管理引用计数以触发指向元素的销毁。boost::shared_ptr 就是这种“指针”的一个示例)

【讨论】:

    【解决方案2】:

    程序运行没有错误,但现在正在创建控件!

    这其实很奇怪,因为在Controls::Controls(const Controls &amp;copy) 中你调用this-&gt;Slider.get() 会返回0,因为你的成员还没有初始化。

    首先,您需要阅读并了解如何使用auto_ptr

    其次,您应该忘记auto_ptr 并且永远不要进一步使用它。 Qt 有自己的智能指针,但在您的情况下不需要它们,因为 QObject 管理其子对象的生命周期。

    因此,第三,阅读 Qt 中的内存管理并彻底摆脱所有这些智能指针。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-06-26
      • 1970-01-01
      • 2022-09-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-21
      相关资源
      最近更新 更多