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

Vector of object pointers and objects going out of scope

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

我有一个循环,其中创建派生 class 的新对象,然后我想将它们放入向量中。但是因为它是一个指针向量,所以我必须把创建的对象的地址放在那里。

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

我举了一个我认为可以说明我的问题的最小示例。假设初始设置如下(我知道它没有多大意义,例如使用指针 *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.

如前所述,我曾尝试使用各种特殊成员函数来执行此操作,但没有成功。

请注意,实现对象本身的向量似乎更容易,但这会导致我的实际代码出现其他问题。如果我不能让它工作,我会试试。

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

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

包装这些指针

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

您将不得不创建新对象。

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<> 到同一对象。