class MyClass
{
};
void foo
{
MyClass arr[10];
}
我想了解函数返回时数组对象销毁的顺序。
我读了《More Effective C++》,它说析构函数的调用顺序与构造函数相反,具体如下:
for(int i = 9 ; i >= 0 ;i--)
{
arr[i].MyClass::~MyClass();
}
有人能告诉我这是为什么吗?(涉及IT技术)
class MyClass
{
};
void foo
{
MyClass arr[10];
}
for(int i = 9 ; i >= 0 ;i--)
{
arr[i].MyClass::~MyClass();
}
这是C++反向析构调用哲学的延续。当栈中分配的对象被销毁时,它们会按照相反的顺序进行销毁,以方便RAII。尽管这对于数组元素来说并不是必要的(它们都是使用默认构造函数构建的,任何构建/销毁顺序都可以),但为了保持一致性,也会按照相同的方式处理它们。
在《More Effective C++》中提到的信息适用于包含其他对象的对象,就像这个例子:
class Foo {
private:
Bar bar_1;
Bar bar_2;
public:
Foo() : bar_1(), bar_2() {}
};
其实任何顺序都可以。显而易见的选择当然是按顺序或按相反顺序。然而,在这种情况下,编译器制造商都认为没有必要留下这个实现依赖项。因此,选择强制使用相反的顺序(如sharptooth所述,扩展了通常的LIFO行为)。
您没有说明Meyer的书中是哪一页,但我同意Timo Geusch的观点,解释似乎是在指出构造函数和析构函数是根据继承调用的。
对于对象实例数组,对象销毁的顺序与构造的顺序相反。这很容易验证,如下面的代码所示。一个类变量跟踪创建的实例总数,每个对象的数据成员跟踪其自己的编号。构造函数和析构函数打印一条消息,因此当运行时我们可以看到发生了什么以及何时发生。
测试代码在构造时从0到9打印对象计数,然后在销毁实例时从9到0递减。(在Mac OS X上使用g++-4.2
进行了测试。)
#include <iostream>
class MyClass
{
public:
MyClass()
{
mCounter = kInstanceCount++;
std::cout << "+++ MyClass() " << mCounter << std::endl;
}
~MyClass()
{
std::cout << "--- MyClass() " << mCounter << std::endl;
}
private:
unsigned mCounter;
static unsigned kInstanceCount;
};
unsigned MyClass::kInstanceCount = 0;
int main()
{
MyClass arr[10];
return 0;
}
你需要检查C++标准,因为我不能百分之百确定这不是一个实现细节(通常情况下是这样),如果是这种情况,你就不应该依赖这种行为。
还要注意的是,创建基于堆栈的实际对象实例数组并不常见。你更可能使用std::vector
,或者使用智能指针来分配堆内存中的对象。