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

时间:2015-05-25 10:46:44

标签: c++ pointers vector polymorphism

我们说我有以下代码,简而言之,有:

  • BaseClass,指针指向PointerClass
  • 继承自ChildClass
  • BaseClass
  • HolderClass std::vectorChildClassstd::vector BaseClass* s:

整个代码如下:

#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方法std::vector<ChildClass> childVec中添加实际addParentClass() {1}},HolderClassbasePointerVec中的数据完全不同。

此外,当我尝试从childVec释放PointerClass es时,一切正常。但是当我尝试将它们从childVec中解放出来时,我得到一个错误,告诉我我试图释放一个我没有为内存分配的指针。

当然,当我使用断点来检查所有内容时,我会发现一些时髦的行为。似乎每次我在basePointerVec中拨打ChildClass childClass = ChildClass();时,addParentClass()中的每个指针都会更改为指向新创建的basePointerVec&#39; s ChildClass

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

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

P.S。对不起这个问题的长度,它尽可能短,

5 个答案:

答案 0 :(得分: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());
}

答案 1 :(得分:1)

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

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

  2. 始终将资源存储在具有RAII原则的类中:

    • 除了一个很好的理由,你永远不应该有一个裸成员指针。请改为使用shared_ptr成员。

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

  3. 使用这个,希望你不需要在你的头脑中追踪在被复制到任何东西之前被破坏的东西,等等。这会使混合堆对象和堆栈对象变得更加困难。

答案 2 :(得分:0)

看这里:

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

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

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

您正在创建一个在addParentClass超出范围时销毁的局部变量。另请参阅this question的答案,其中说明了当您不使用new时会发生什么。因此,BaseClass指针的向量指向被销毁的对象,当childVec使用push_back时,import java.awt.Frame; import java.awt.Graphics; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; //Create a frame window that responds to mouse click public class AWT3 extends Frame { String Mmsg=""; int mouseX=0, mouseY=0; public AWT3() { addWindowListener(new MyWindowwAdapter(this)); addMouseListener(new MyMouseeAdapter(this)); } public void paint(Graphics g){ g.drawString(Mmsg, mouseX, mouseY); } public static void main(String args[]){ AWT3 awt3 = new AWT3(); awt3.setSize(new dimension(500, 500)); awt3.setTitle("Window framee"); awt3.setVisible(true); } } class MyWindowwAdapter extends WindowAdapter{ AWT3 awt3; public MyWindowwAdapter(AWT3 awt3) { this.awt3=awt3; } public void windowClosing(WindowEvent we){ awt3.setVisible(false); } } class MyMouseeAdapter extends MouseAdapter{ AWT3 awt3; public MyMouseeAdapter(AWT3 awt3) { this.awt3=awt3; } public void MouseClicked(MouseEvent me){ awt3.Mmsg="the mouse is clicked"; awt3.mouseX= me.getX(); awt3.mouseY=me.getY();`` awt3.repaint(); } } 对象向量正在创建新副本:< / p>

  

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

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