c++中的多态类指针

C++ polymorphic class pointer in vector

本文关键字:指针 多态 c++      更新时间:2023-10-16

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

  • 指向PointerClassBaseClass
  • 继承BaseClassChildClass
  • HolderClass具有ChildClass s的std::vectorBaseClass* s的std::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;
}

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

此外,当我尝试从childVec中释放PointerClass时,一切都工作正常。但是当我试图从basePointerVec中释放它们时,我得到一个错误,告诉我我正在试图释放一个我没有分配内存的指针。

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

我在实际程序中这样做的目的是利用多态性,并从BaseClass继承多个类。

所以我的问题是,为什么每个指针在向量被改变为指向新创建的类,我怎么能修复它?

注:很抱歉这个问题太长了,我已经尽可能的短了

    childVec.push_back(childClass);

类vector的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. 当你定义一个类,特别是一个可以被子类化的类时,除非你有特殊的原因不能这样做,否则总是让医生public virtual

  2. 始终按照RAII原则在类中存储资源:

    • 除非有很好的理由,否则永远不应该使用裸成员指针。使用shared_ptr成员代替。

    • 除非有很好的理由,否则你永远不应该有一个裸指针的vector。再次使用共享指针的向量


使用这个,希望,你不需要在你的头脑中跟踪什么东西被破坏了,当一些东西被复制到任何东西,等等。这将使混淆堆对象和堆栈对象变得更加困难。

看这里:

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

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

addParentClass返回时,childClass对象被销毁。因此,您放入basePointerVec中的指向该对象的指针不能再使用了。

您正在创建一个局部变量,一旦addParentClass超出范围就会被销毁。也可以看看这个问题的答案,它解释了当你不使用new时会发生什么。因此,BaseClass指针的向量指向一个被销毁的对象,而childVec指针的对象向量在使用push_back时正在创建新的副本,从这个页面:

新元素初始化为value的副本。

这就是为什么两个向量指向不同的对象。您可以为类创建析构函数,并在析构函数和构造函数中添加调试打印,以查看对象何时被创建/销毁,这将使您更好地了解以何种顺序发生了什么。