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

Vector of object pointers and objects going out of scope

本文关键字:对象 范围 指针      更新时间:2023-10-16

我正在使用一个派生类和指向该类中对象的指针向量。我在实现复制构造函数等方面遇到了一些问题,即使在读了很多关于它的文章之后也是如此。我使用的是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中的地址。

如前所述,我尝试过使用各种特殊的成员函数来实现这一点,但没有成功。

请注意,实现对象向量本身似乎更容易,但这会在我的实际代码中导致其他问题。如果我不能做到这一点,我会试试看。

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

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

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

您将不得不创建新对象。

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