向量中的 C++ 多态 class 指针

C++ polymorphic class pointer in vector

假设我有以下代码,简而言之,它有:

整个代码如下:

#include <stdlib.h>
#include <time.h>
#include <vector>

class PointerClass {
public:
    int num;
    double num2;
    PointerClass() {
        srand(time(NULL));
        num = rand() % 100;
        num2 = rand() % 100 / 2.0;
    }
};


class BaseClass {
public:
    PointerClass *pointerClass;
};


class ChildClass: public BaseClass {
public:
    ChildClass() {
        pointerClass = new PointerClass();
    }
};


class HolderClass {
public:
    std::vector<BaseClass*> basePointerVec;
    std::vector<ChildClass> childVec;

    HolderClass() {

    }

    void addParentClass() {
        ChildClass childClass = ChildClass();

        childVec.push_back(childClass);
        basePointerVec.push_back(&childClass);
    }
};


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

    HolderClass holderClass = HolderClass();

    for (int count = 0; count < 20; count++) {
        holderClass.addParentClass();
    }

    for (int count = 0; count < holderClass.basePointerVec.size(); count++) {
        delete holderClass.basePointerVec[count]->pointerClass;
    }

    return 0;
}

我的问题是,在将指向 ChildClass 的指针添加到 std::vector<BaseClass*> basePointerVec 并将实际的 ChildClass 添加到 addParentClass() 方法中的 std::vector<ChildClass> childVec 之后在HolderClass中,basePointerVecchildVec中的数据完全不同。

此外,当我尝试从 childVec 中释放 PointerClasses 时,一切正常。但是,当我尝试从 basePointerVec 中释放它们时,我收到一条错误消息,告诉我我正在尝试释放一个我尚未为其分配内存的指针。

果然,当我使用断点检查所有内容时,我发现了一些奇怪的行为。似乎每次我在 addParentClass() 中调用 ChildClass childClass = ChildClass(); 时,basePointerVec 中的每个指针都会更改为指向新创建的 ChildClassBaseClass

我在实际程序中这样做的目的是利用多态性并让多个 class 继承自 BaseClass

所以我的问题是,为什么向量中的每个指针都被更改为指向新创建的 class,我该如何修复它?

P.S。很抱歉这个问题的长度,这是我能做到的最短

看这里:

void addParentClass() {
    ChildClass childClass = ChildClass();

    childVec.push_back(childClass);
    basePointerVec.push_back(&childClass);
}

您的对象没有分配到堆上。它们是值,在 basePointerVec 中放置它们的地址(在 addParentClass() returns 之后无论如何都没有意义)。不要试图删除那些,那会使你的程序崩溃。当 childVec 超出范围时,它们将被自动删除。

addParentClassreturns时,childClass对象被销毁。因此,您放入 basePointerVec 的那个对象的指针不能再使用了。

您正在创建一个局部变量,一旦 addParentClass 超出范围,该变量就会被销毁。另请参阅 this question which explains what happens when you don't use new. So the vector of BaseClass pointers are pointing to an object that is destroyed, and the childVec vector of objects are creating new copies when you use push_back, from this page:

的答案

The new element is initialized as a copy of value.

这就是两个向量指向不同对象的原因。您可以为 类 创建一个析构函数,并将调试打印添加到析构函数和构造函数以查看对象何时为 created/destroyed,这应该让您更好地了解按什么顺序发生的事情。

    childVec.push_back(childClass);

class向量的push_back方法复制对象。所以在这种情况下添加的对象与 childClass 不同。

您不能从 basePointerVec 中删除指针,因为它们不是用 new 分配的,而是在本地分配的,并且 它们在 addParentClass 的末尾被删除。因此,addParent 的代码是错误的,因为您在向量中推送的指针在该方法结束后不再有效,并且可能导致分段错误(在最好的情况下)。这里有一个改进建议:

void addParentClass() {
    ChildClass* childClass = new ChildClass();

    childVec.push_back(*childClass);
    basePointerVec.push_back(childClass);
}

现在内存是动态分配的,你应该确保用 delete 释放这些指针。

编辑:

void addParentClass() {
    childVec.push_back(ChildClass());
    basePointerVec.push_back(&childVec.back());
}

如果您使用的是 C++11:

void addParentClass() {
    childVec.emplace_back();
    basePointerVec.push_back(&childVec.back());
}

其他答案已经解决了您代码中的具体问题。以下是关于您上面的程序的两个一般要点,它们将帮助您恕我直言,避免将来出现各种问题:

  1. 当你定义一个class时,特别是。一个可以被 subclassed 的,总是使 dtor public virtual 除非你有特定的原因。

  2. 始终将资源存储在 classes 中,遵循 RAII 原则:

    • 除非有充分的理由,否则您永远不应该拥有裸成员指针。请改用 shared_ptr 成员。

    • 除非有充分的理由,否则您永远不应该拥有 vector 裸指针。再次使用共享指针向量。


使用这个,希望您不需要在脑海中跟踪在将某些内容复制到任何内容之前什么时候被破坏了,等等。这将使混合堆对象和堆栈对象变得更加困难。