【问题标题】:C++ polymorphic class pointer in vector向量中的 C++ 多态类指针
【发布时间】:2015-05-25 10:46:43
【问题描述】:

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

  • 带有指向PointerClass 的指针的BaseClass
  • 一个继承自BaseClassChildClass
  • 具有std::vectorChildClasss 和std::vectorBaseClass*s 的HolderClass

整个代码如下:

#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&lt;BaseClass*&gt; basePointerVec 和实际的ChildClassaddParentClass() 方法中HolderClass 中的std::vector&lt;ChildClass&gt; childVec 之后,basePointerVec 中的数据和childVec 完全不同。

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

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

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

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

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

【问题讨论】:

  • 基类中的虚拟析构函数!!
  • 你真的不想这样做。即使您理顺了对象的创建和寻址并使用了basePointerVec.push_back(&amp;childVec.back());,只要childVec 扩展并为扩展的空间分配不同的内存床,您仍然会受到伤害。 basePointerVec 中的所有指针都将失效。

标签: c++ pointers vector polymorphism


【解决方案1】:
    childVec.push_back(childClass);

类向量的 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());
}

【讨论】:

  • 这仍然没有得到 OP 想要的东西。他希望childVec 向量中的项目由basePointerVec 中的指针寻址。您在此处发布的代码将在每次调用时创建 两个 ChildClass 实例;一个通过复制构造推入childVec,另一个指向basePoniterVec
  • 我编辑了这篇文章。我在回答中忘记了这一点。对了,OP是什么意思?
  • @WhozCraig 我相信原始解决方案对我有用。只要每个向量中的数据相同,我不需要childVec 中的项目指向basePointerVec。正如您在上面的评论中提到的那样,这样做是非常错误的。我也会按照@AmiTavory 的建议调查shared_ptr。虽然这里的每个答案都是正确的,但我会将其标记为已接受的答案。感谢大家的帮助!
  • 我应该注意,为了使用原始解决方案,必须给BaseClass 一个虚拟析构函数virtual ~BaseClass {};,以便可以调用delete basePointerVec[number]。有关详细信息,请参阅this 帖子。
  • @glazed4444 “我不需要 childVec 中的项目指向 basePointerVec” - 实际上是您原始帖子显示的方式,它会是相反的方式。 childVec 没有“指向”任何东西;它容纳了实际的物体。我说的是真的:如果调整该向量的大小并使用新的内存床,则基本指针向量将被 dangling 指针填充。
【解决方案2】:

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

  1. 当你定义一个类时,尤其是。一个可以子类化的,总是使用 dtor public virtual 除非你有特定的原因。

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

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

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


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

【讨论】:

    【解决方案3】:

    看这里:

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

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        您正在创建一个局部变量,一旦addParentClass 超出范围,该变量就会被销毁。另请参阅this question 的答案,其中解释了不使用new 时会发生什么。所以BaseClass指针的向量指向一个被销毁的对象,当你使用push_back时,对象的childVec向量正在创建新的副本,来自this page

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-01-02
          • 2019-09-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-04-13
          • 1970-01-01
          相关资源
          最近更新 更多