0

我有 thou 对象:

Polygon p1, p2;

我有一个Polygon被调用的继承类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第二次运行析构函数。

所以在代码结束后,这就是调试器所说的p1n是边数):

p1  {n=3 sides=0x0062c070 {-17891602} } Polygon

问题:

  • 为什么它调用析构函数?
  • 为什么同时调用TriangleandPolygon的析构函数?
  • 如何解决这个问题?

编辑:根据要求:

/*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;
}

另外,感谢您解释它为什么运行~Polygon,将考虑在内。

4

3 回答 3

2

这一行:

p1 = Triangle(temp1, temp2, temp3);

构造一个三角形对象,然后在 p1 中制作该对象的副本,然后销毁原始对象。它是您看到的原始对象的析构函数。(而且它可能无论如何都不会做你想做的事情,因为它会分割对象并只存储基类具有的部分。)

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

作为风格问题

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

在删除它之前不需要测试边,因为空指针上的 delete 在设计上没有任何作用。并且几乎没有将边设置为 NULL 的意义,因为在运行析构函数之后,对象无论如何都不存在。

于 2013-05-14T09:32:11.683 回答
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 对象到作为 Polygon 的 p1 的复制只是像 Polygon 一样复制。因此,“复制”的 p1 不包含 Triangle 的任何信息。


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

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

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

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

于 2013-05-14T09:55:44.787 回答
0

由于您仍在学习并且其他人回答了您的问题,这里有一些关于 C++ 编程的建议。

Polygon p1, p2;

这是 Polygon 类的两个对象。

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

这构造了一个 class 的对象Triangle,并调用了一个赋值运算符p1来从 a 构造它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;
    }
}

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

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

如果它可能没有值,仍然总是删除它。Delete 对于空指针值是安全的,从而使代码更简洁。这将完全删除您的支票。将其设置为NULL是允许的,这将有助于找到指向该Polygon对象的悬空指针的使用。

于 2013-05-14T09:56:18.757 回答