对象指针和对象的向量超出范围

时间:2016-03-29 14:37:17

标签: c++ pointers c++11 vector copy-constructor

我正在使用派生类和指向来自所述类的对象的指针向量。我在实现复制构造函数等方面遇到了一些问题,即使在阅读了很多相关内容之后也是如此。我正在使用c ++ 11。

我有一个循环,其中创建了派生类的新对象,然后我想将它们放入向量中。但由于它是一个指针向量,我必须将创建对象的地址放在那里。

我相信我的问题源于这个事实,以及超出范围等的对象以及我无法以令人满意的方式实现复制构造函数/复制赋值构造函数等。

我做了一个最小的例子,我认为这说明了我的问题。假设初始设置如下(我知道它没有多大意义,例如使用指针*n,但我认为这显示了我实际代码的问题):

using namespace std;
#include <iostream>
#include <vector>

class Base {

    protected:
    int* n;

    public:
    void display(string name="") {cout << name << "n = " << *n << endl;}

    Base() {}
    Base(int n_) {
        *n=n_;
        cout << "Initialised to " << *n << endl;
    }
};

class Derived : public Base {

    public:

    Derived() : Base() {}
    Derived(int n_) : Base(n_) {}
};

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

    vector<Base*> list;
    for(int i=0;i<5;i++) {
        Derived tmp(i);
        tmp.display("Tmp: ");
        list.push_back(&tmp);
    }

    cout << endl;

    for(int i=0;i<list.size();i++) {
        cout << "Address of " << i << ": " << list[i] << endl;
        list[i]->display("Element "+to_string(i)+" : ");
    }

}

这个输出是:

Initialised to 0
Tmp: n = 0
Initialised to 1
Tmp: n = 1
Initialised to 2
Tmp: n = 2
Initialised to 3
Tmp: n = 3
Initialised to 4
Tmp: n = 4

Address of 0: 0x7fff3a1df2d0
Element 0 : n = 0
Address of 1: 0x7fff3a1df2d0
Element 1 : n = 0
Address of 2: 0x7fff3a1df2d0
Element 2 : n = 0
Address of 3: 0x7fff3a1df2d0
Element 3 : n = 0
Address of 4: 0x7fff3a1df2d0
Element 4 : n = 0

因此在循环之后,列表中的所有元素都指向同一个地址,其中n = 0(可能是tmp中未定义的行为超出范围)。

所以,而不是仅仅将tmp的地址放入list我想我不得不让Derived的实例在循环中存活,同时仍然只有list中的地址1}}。

如上所述,我尝试使用各种特殊的成员函数来做这件事而没有运气。

请注意,似乎实现对象本身的向量可能更容易,但这会在我的实际代码中导致其他问题。如果我不能正常工作,我会试着这样做。

2 个答案:

答案 0 :(得分:4)

它与复制或复制构造函数无关,它只是因为对象超出范围并被破坏,而你仍然保留指向这些(现在被破坏的)对象的指针。当您尝试取消引用这些指针时,这会导致未定义的行为

相反,你需要使用例如动态分配这些对象。 new。您可以使用C++11 smart pointers包装这些指针。

答案 1 :(得分:2)

将指向堆栈中对象的指针添加到向量中。当前作用域结束时,这些对象将被销毁,但指针仍在那里。

您必须改为创建新对象。

int main(int argc, const char* argv[]) {
    vector<Base*> list;
    for(int i=0;i<5;i++) {
        auto tmp = new Derived{i};
        tmp->display("Tmp: ");
        list.push_back(tmp);
    }
    // ...
    }
}

现在您仍需要确保,您可以根据需要释放对象。只要有可能,请选择unique_ptr<>shared_ptr<>

int main(int argc, const char* argv[]) {
    vector<unique_ptr<Base>> list;
    for(int i=0;i<5;i++) {
        auto tmp = make_unique<Derived>(i);
        tmp->display("Tmp: ");
        // this should move the unique_ptr and therefore transfer 
        // its ownership to the vector.
        list.emplace_back(tmp);
    }
    // ...
    }
}

现在,对象将被销毁,无论是从向量中移除还是向量被销毁。使用可能会延迟的shared_ptr,直到您的程序中没有任何部分将shared_ptr<>保存到同一个对象。