它将执行相同的操作(实质上是什么都不做)。但这并不等同于您未编写它。因为编写析构函数需要一个正常工作的基类析构函数。如果基类析构函数是私有的或者由于其他原因无法调用,则您的程序是有缺陷的。请考虑以下内容。
struct A { private: ~A(); };
struct B : A { };
只要您不需要销毁类型为B的对象(因此也就是隐式销毁类型A的对象) - 就像从未对动态创建的对象调用delete,或者根本不先创建它一样,那么这是可以的。如果您这样做了,编译器将显示相应的诊断信息。现在,如果您明确提供一个对象,则同样适用。
struct A { private: ~A(); };
struct B : A { ~B() { } };
那个会尝试隐式调用基类的析构函数,并且会在定义
~B
时就导致诊断问题。
还有一个区别,围绕着析构函数的定义和成员析构函数的隐式调用。考虑这个智能指针成员。
struct C;
struct A {
auto_ptr<C> a;
A();
};
假设在A的构造函数定义中创建了类型为C的对象,并且该构造函数定义还包含struct C的定义,现在如果使用struct A并要求销毁A对象,则编译器将提供析构函数的隐式定义,就像上面的情况一样。该析构函数也会隐式调用auto_ptr对象的析构函数。这将删除指向C对象的指针,而不知道C的定义!这出现在定义struct A的构造函数的.cpp文件中。
实际上,这是实现pimpl习惯的常见问题。解决方法是添加一个析构函数并在定义struct C的.cpp文件中提供一个空定义。在调用其成员的析构函数时,它将知道struct C的定义,并可以正确调用其析构函数。
struct C;
struct A {
auto_ptr<C> a;
A();
~A();
};
请注意,boost::shared_ptr没有这个问题:在某些情况下调用其构造函数时需要完整的类型。
当前C++中另一个区别的点是,当你想在具有用户声明析构函数的对象上使用memset和friends时。这些类型不再是POD(plain old data),因此不允许进行位复制。请注意,这种限制实际上并不是必需的 - 下一个C++版本已经改进了这种情况,因此它允许您仍然可以位复制这些类型,只要不进行其他更重要的更改。
由于您要求构造函数:对于这些内容,情况基本相同。请注意,构造函数还包含对析构函数的隐式调用。对于像auto_ptr这样的东西,即使在运行时实际上并没有执行这些调用(在这里,纯粹的可能性已经很重要),它们会像析构函数一样造成伤害,并且当构造函数中的某些内容抛出异常时发生 - 编译器随后需要调用成员的析构函数。这个答案利用了默认构造函数的隐式定义。
此外,与我上面提到的析构函数相同,可见性和PODness也是如此。
关于初始化,有一个重要的区别。如果您放置了一个用户声明的构造函数,则您的类型不再接收成员的值初始化,而是由您的构造函数来进行任何所需的初始化。例如:
struct A {
int a;
};
struct B {
int b;
B() { }
};
在这种情况下,以下内容始终为真。
assert(A().a == 0);
尽管以下是未定义的行为,因为
b
从未初始化(您的构造函数省略了这一点)。该值可能为零,但也可能是任何其他奇怪的值。尝试从这样一个未初始化的对象中读取会导致未定义的行为。
assert(B().b == 0);
对于使用new
语法,如new A()
也是如此(请注意括号在末尾——如果省略了括号,则不会进行值初始化,由于没有用户声明的构造函数可以进行初始化,因此a
将被保留未初始化状态)。