C++ 多态性问题。与 _vfptr 有关

C++ Polymorphism issues. Something to do with _vfptr

我正在尝试创建一个程序,让用户在屏幕上点击两次,然后根据点击的内容绘制一个矩形。

现在我要做的就是设置我的 classes 以便能够正确地手动绘制矩形而不用担心用户点击的位置。

最终我的程序将能够绘制圆形和三角形之类的东西,所以我决定使用多态性并让每个形状类型(即 Rectangle)成为它自己的 class,继承自 class 称为 Shapes.

然后我有一个名为 Game 的 class,它包含一个 Shapes 对象。

Shapes.h

#ifndef _shapes_h_
#define _shapes_h_
#include <vector>
#include "glut.h"

class Shapes
{
public:
    void DrawAll() const;
    void Add(Shapes * shape);
    virtual void Draw() const {}

protected:   
    std::vector<Shapes *> mShapes;
};

#endif  

Shapes.cpp

#include "Shapes.h"
#include <iostream>

void Shapes::DrawAll() const
{
    int i;
    for (i = 0; i < mShapes.size(); i++)
    {
        mShapes[i]->Draw();
    }
}

void Shapes::Add(Shapes * shape)
{
    mShapes.push_back(shape);
}

Rectangle.h

#ifndef _rectangle_h_
#define _rectangle_h_
#include "Shapes.h"

class Rectangle : public Shapes
{
public:
    Rectangle(std::vector<int> p1, std::vector<int> p2);
    void Draw() const;

private:
    std::vector<int> mP1;
    std::vector<int> mP2;
};

#endif

Rectangle.cpp

#include "Rectangle.h"
#include <iostream>

Rectangle::Rectangle(std::vector<int> p1, std::vector<int> p2)
{
    mP1 = p1;
    mP2 = p2;
}


void Rectangle::Draw() const
{
    std::cout << "Draw Me " << std::endl;
    glColor3d(0, 0, 0);

    glBegin(GL_QUADS);
    glVertex2d(mP1[0], mP1[1]);
    glVertex2d(mP2[0], mP1[1]);
    glVertex2d(mP2[0], mP2[1]);
    glVertex2d(mP1[0], mP2[1]);
    glEnd();
}

Game.h

#ifndef _game_h_
#define _game_h_

#include <vector>
#include "Shapes.h"

class Game
{
public:
    void Click(int x, int y);
    void Draw();

private:    
    Shapes mTest;
};

#endif

下面的第一个 Game.cpp 是按照我正在尝试做的方式编码的,但它抛出了错误 Unhandled exception at 0x001AF742 in Shapes.exe: 0xC0000005: Access violation reading location 0x00000001. 在调查之后,我发现一些 __vfptr 隐藏的指针变量(我认为它控制虚拟的东西)被设置为 0 并导致内存问题。

注意,Click() 仅在按下鼠标时调用,Draw() 在任何类型的事件发生时调用(e.i。鼠标按下、鼠标释放、按键、键释放等)按下鼠标时,两个事件都会被调用,但 Click() 首先被调用。

此外,mTest是作为 Shapes 对象的变量。

Game.cpp:不起作用

#include "Game.h"
#include "Rectangle.h"
#include <iostream>

void Game::Click(int x, int y)
{     
    std::vector<int> p1;
    p1.push_back(200);
    p1.push_back(200);

    std::vector<int> p2;
    p2.push_back(250);
    p2.push_back(250);

    Rectangle rect(rp1, rp2);

    Shapes * rectangle = &rect;
    mTest.Add(rectangle);
}

void Game::Draw()
{       
    mTest.DrawAll();
}

然而,令我困惑的是,如果我在调用 DrawAll() 之前向 Draw() 函数内部的 mTest 添加一个 Shape Rectangle,它就可以工作。但是,我希望能够根据用户点击的位置创建矩形,而此方法不允许这样做。

Game.cpp:有效

#include "Game.h"
#include "Rectangle.h"
#include <iostream>

void Game::Click(int x, int y)
{     
}

void Game::Draw()
{
    std::vector<int> p1;
    p1.push_back(200);
    p1.push_back(200);

    std::vector<int> p2;
    p2.push_back(250);
    p2.push_back(250);

    Rectangle rect(rp1, rp2);

    Shapes * rectangle = &rect;
    mTest.Add(rectangle);
    mTest.DrawAll();
}

在形状中 class 替换

std::vector<Shapes *> mShapes; 

来自

std::vector<std::shared_pointer<Shapes>> mShapes; 

比替换代码

Rectangle rect(rp1, rp2);

Shapes * rectangle = &rect;
mTest.Add(rectangle);

来自

mTest.Add(std::make_shared<Rectangle>(rp1, rp2));

最后替换方法定义

void Shapes::Add(Shapes * shape)

来自

void Shapes::Add(std::shared_ptr<Shapes> shape)

它使您的代码有效。

总结:如果需要指针,可以考虑智能指针(@user657267提到的std::unique_ptr也是智能指针)。你可以在很多地方读到智能指针,here and here。为了更好的设计,不要忘记设计模式。