class Element {
public:
ElementTypes type = DOT;
Element() {}
Element(ElementTypes type) : type(type) {}
virtual void Draw() { return; }
};
class Dot : public Element {
public:
int x, y;
Dot(int x, int y) : x(x), y(y) {}
void Draw() override {
DrawCircle(x, y, 2.f, BLACK);
}
};
class Drawing {
public:
std::vector<Element*> Elements;
void AddDot(Dot& dot) {
Elements.emplace_back(&dot);
}
void Draw() {
for (auto element : Elements) {
element->Draw();
}
}
};
由于某种原因,在尝试调用element->Draw()
时出现崩溃。
Exception thrown at 0x00007FF66DDC1486 in geometry.exe: 0xC0000005: Access violation reading location 0x0000000000000000.
我正在使用函数AddDot
向向量添加元素
不使用指向类的指针,Draw
函数就是没有被覆盖。
问题确实可能是添加到矢量中的dot
在绘制时已不存在。典型的情况是,如果您在函数中初始化Drawing
,则使用在离开初始化函数时被破坏的本地对象。
解决该问题的一种方法是确保在Drawing
中添加的所有图形都是在具有new
的自由石上创建的。你以后仍然需要处理它们的删除。这很容易出错。
一个更好的选择是使用智能指针而不是原始指针。这确保了只要有一些智能指针指向该对象,该对象就会保持活动状态,并且一旦不再使用该对象,就会销毁该对象(提示:使用Drawing的析构函数。此处更改:
class Drawing {
public:
std::vector<shared_ptr<Element>> Elements;
void AddDot(shared_ptr<Element> dot) {
Elements.emplace_back(dot);
}
void Draw() {
for (auto element : Elements) {
element->Draw();
}
}
};
void init (Drawing&d) {
auto dt1 = make_shared<Dot>(10,30);
d.AddDot(dt1);
}
int main() {
Drawing d;
init (d);
d.Draw();
}
(在线演示(
优点是可以安全地将AddDot()
转换为多态AddElement()
,避免为每个可能的形状创建新的添加函数。
另一种方法没有智能指针那么复杂和健壮,但相对容易实现:将传递的点作为参数,并制作一个由Drawing
拥有(稍后销毁(的副本。不幸的是,对于您当前的代码,这将是麻烦的,因为您需要为想要添加的Element
的每个子类使用不同的函数(因为它们的构造函数可能采用不同的参数,并且您不希望发生对象切片(。因此,您需要为Element
提供一个clone()
函数,该函数返回一个指向新创建的相同类型的Element
的指针(这是原型模式(。
欢迎使用您的第一次访问违规
不要惊慌。:(
Access violation reading location 0x0000000000000000
访问违规+像0x0000(零(这样的位置可能只是nullptr
。既然你拿着std::vector<Element*> Elements;
这样的东西,它从哪里来可能是可以理解的。
要添加新的Element
,您需要实际分配Dot& dot
的使用通过引用将其传递到未知位置,这不是一个好的做法,因为您会很快到达UB(或者更糟的是,堆损坏(。
要分配一个新元素,您需要执行以下操作:
std::vector<Element*> Elements;
Elements.push_back(new Element(/*Params here*/));
由于你的课上实际上没有任何昂贵的价值,你可以按价值持有:
std::vector<Element> Elements
为了简单起见,不要探究为什么它应该是值或指针
将其与Composition相结合,为了矢量的简单性,您可以编写一个绘图:
struct Drawable
{
Drawable(Entity* toDraw) :ToDraw(toDraw) {}
~Drawable() { if(ToDraw != nullptr) { delete ToDraw; } }
Entity* ToDraw;
void Draw(){ ToDraw.Draw(); }
}
// Vector doesn't hold pointers anymore.
// Passing Drawable& will not result in Undefined behavior but a copy or a move.
std::vector<Drawable> toDrawVec;
toDrawVec.emplace_back(new Dot());
toDrawVec.emplace_back(new Square());
toDrawVec.emplace_back(new Square());
请专注于您的学习,并尝试查找unique_ptr
、shared_ptr
和RAII
(此处使用的技术(。
虚拟函数在这里不起作用,因为Drawing
没有继承Element
我建议制作类似Drawable
的东西,这样任何需要绘制的实体都可以继承它。
祝你好运!