对象在=操作之前超出范围

时间:2013-05-14 09:28:18

标签: c++ class inheritance

我有很多东西:

Polygon p1, p2;

我有一个名为Polygon的{​​{1}}继承类,我尝试这样做:

Triangle

但由于某种原因,p1 = Triangle(temp1, temp2, temp3); // temp 1,2,3 are lengths of sides 的析构函数在构造结束时被调用。

Triangle

然后它再次运行Rectangle::~Rectangle(void) { Polygon::~Polygon(); } Polygon::~Polygon(void) { if (sides != NULL) { delete [] sides; sides = NULL; } } 的析构函数。

所以在代码结束后,这是调试器对Polygon所说的内容(p1是边数):

n

问题:

  • 为什么要调用析构函数?
  • 为什么同时调用p1 {n=3 sides=0x0062c070 {-17891602} } Polygon Triangle的析构函数?
  • 如何解决这个问题?

编辑: 按要求:

Polygon

另外,感谢解释为什么它运行/*returns true if for the polygons A and B: (a) for each side on polygon A there is an equal side on polygon B (b) the number of sides in polygons A and B are equal (c) sum of sides of A equals the sum of sides of B */ bool Polygon::operator==(const Polygon& p) const { if (n != p.n) return false; if(circumference() != p.circumference()) return false; for (int i=0; i < n; i++) if (!doesSideHasEqual(sides[i], p, n)) return false; return true; } ,将考虑到这一点。

3 个答案:

答案 0 :(得分:2)

这一行:

p1 = Triangle(temp1, temp2, temp3);

构造一个三角形对象,然后在p1中创建该对象的副本,并销毁原始对象。它是您所看到的原始对象的析构函数。 (它可能不会做你想要的,因为它会切割对象,只存储基类所具有的部分。)

此外,您不应该在析构函数中调用基类的析构函数,它们会自动调用。

作为一种风格问题

if (sides != NULL)
{
    delete [] sides;
    sides = NULL;
}

在删除之前不需要测试边,因为空指针上的删除在设计上没有任何作用。

表示,在析构函数运行后,对象根本不存在

答案 1 :(得分:0)

Polygon p1;

p1 = Triangle(temp1, temp2, temp3); // temp 1,2,3 are lengths of sides

实际上是在给你超出预期的结果。

你想做的事情就像是

Polygon& p1;
Triangle t(temp1, temp2, temp3);

p1 = t;

Polygon* p1 = new Triangle(temp1, temp2, temp3);

中的问题
p1 = Triangle(temp1, temp2, temp3);

不仅是Triangle对象是临时的并且在声明之后就被销毁了,但是,更严重的是,从Triangle对象复制到p1这是Polygon,只是复制像Polygon一样。因此,“复制”p1不包含Triangle的任何信息。


另一个问题是你编写析构函数的方式。 C ++中的析构函数是自动链接的(我认为你来自Java背景,我们需要显式调用超类'终结器)

Rectangle::~Rectangle(void)
{
    Polygon::~Polygon();  // can be removed
}

因此,您不应该调用Polygon的析构函数。

析构函数的另一个问题是,如果你的类应该被继承(你的Polygon是一个很好的例子),你应该声明你的析构函数是虚拟的。出于背后的原因,搜索“虚拟析构函数”。

答案 2 :(得分:0)

当你还在学习并且其他人回答你的问题时,这里有一些关于C ++编程的指示。

Polygon p1, p2;

这是Polygon类的两个对象。

p1 = Triangle(temp1, temp2, temp3); // temp 1,2,3 are lengths of sides

这构造了类Triangle的对象,并在p1上调用赋值运算符以从Triangle构造它。 Triangle透明地向上转换为Polygon并复制构造为p1,之后Triangle临时对象将被销毁。

Rectangle::~Rectangle(void)
{
    Polygon::~Polygon();
}

这有一个非常重大的错误和一个微妙的“老式人”的事情。

从不需要在代码中提及(void);这在大约14年前的C中才是明智的。我们正在做C ++,我们在2013年这样做,所以不要使用(void)但只使用()。这已明确表示不允许任何参数。

但主要的问题是你明确地破坏了父母。父母总是被隐含地破坏。这样它们首先被明确地破坏,然后再次被隐含地破坏。永远不要从析构函数中调用析构函数。

Polygon::~Polygon(void)
{
    if (sides != NULL)
    {
        delete [] sides;
        sides = NULL;
    }
}

这是“防御性编程”的热潮。代码可以多次调用,也可以在半构造的对象上调用,不会崩溃。另一方面,您也不会在软件的其他部分中暴露错误,因此这些错误将持续存在。双 - delete通常指向共享所有权,并通过扩展指向悬空指针。这将完美掩盖该错误。

要么确定sides成员确实有值,要么没有。如果它有值,请始终将其删除。为了安全起见,添加一个断言sides != nullptrsides != NULL来预先检查它,这样如果核心文件/堆栈跟踪没有,代码就会崩溃。

如果它没有值,仍然总是删除它。删除对于空指针值是安全的,从而使代码更加清晰。这完全取消了您的支票。允许将其设置为NULL,这将有助于查找对此Polygon对象的悬空指针的使用。