您的位置:首页 > 其它

关于隐式提供缺省构造函数的一个误区

2007-08-05 17:10 253 查看
很多C++的教材中都讲:“如果一个定义一个类,并且不提供任何构造函数的话,那么编译器将会隐式的提供一个缺省构造函数”。
以下节录ISO C++ 99的文档的原文:
The default constructor (12.1), copy constructor and copy assignment operator (12.8), and destructor (12.4)
are special member functions. The implementation will implicitly declare these member functions for a
class type when the program does not explicitly declare them, except as noted in 12.1. The implementation
will implicitly define them if they are used, as specified in 12.1, 12.4 and 12.8.

A default constructor for a class X is a constructor of class X that can be called without an argument. If
there is no user-declared constructor for class X, a default constructor is implicitly declared. An implicitly-
declared default constructor is an inline public member of its class.

An implicitly-declared default constructor for a class is implicitly defined when it is used to create an object
of its class type (1.8). The implicitly-defined default constructor performs the set of initializations of the
class that would be performed by a user-written default constructor for that class with an empty meminitializer-list (12.6.2) and an empty function body. If that user-written default constructor would be illformed,the program is ill-formed. Before the implicitly-declared default constructor for a class is implicitly
defined, all the implicitly-declared default constructors for its base classes and its nonstatic data members
shall have been implicitly defined. [Note: an implicitly-declared default constructor has an exceptionspecification(15.4).

以下摘录自“孙鑫vc教程的勘误表”:

我说:“C++又规定,如果一个类没有提供任何的构造函数,则C++提供一个默认的构造函数(由C++编译器提供)”,这句话也是错误的,正确的是:
如果一个类中没有定义任何的构造函数,那么编译器只有在以下三种情况,才会提供默认的构造函数:
1、如果类有虚拟成员函数或者虚拟继承父类(即有虚拟基类)时;
2、如果类的基类有构造函数(可以是用户定义的构造函数,或编译器提供的默认构造函数);
3、在类中的所有非静态的对象数据成员,它们对应的类中有构造函数(可以是用户定义的构造函数,或编译器提供的默认构造函数)。

C++ Primer 14.2中也有这样的话:
新用户常常会错误地认为:如果不存在缺省构造函数,则编译器会自动生成一个缺省构
造函数,并将其应用在对象上,以初始化类的数据成员,对于我们定义的Account 类来说这
就不是真的,系统既没有生成缺省构造函数也没有调用它。对于含有类数据成员或继承来的
比较复杂的类,这在部分上是对的,可能会生成一个缺省构造函数,但是它不会为内置或复
合型的数据成员如指针或数组提供初始值。

也可以从底层分析一下:
C/C++ code
class A
{
public:
int a;
char *ptr;
};

int main()
{
A a;
return 0;
}

这种代码不会产生默认的ctor,此时的类就貌似C下面的struct,没有一个成员需要初始化。
证据1:
Assembly code
97:   int main()
98:   {
0040D680   push        ebp
0040D681   mov         ebp,esp
0040D683   sub         esp,48h
0040D686   push        ebx
0040D687   push        esi
0040D688   push        edi
0040D689   lea         edi,[ebp-48h]
0040D68C   mov         ecx,12h
0040D691   mov         eax,0CCCCCCCCh
0040D696   rep stos    dword ptr [edi]
99:       A a;                  //什么都没做
100:      return 0;
0040D698   xor         eax,eax
101:  }
0040D69A   pop         edi
0040D69B   pop         esi
0040D69C   pop         ebx
0040D69D   mov         esp,ebp
0040D69F   pop         ebp
0040D6A0   ret

但是,
(1)如果加入需要构造函数的成员变量,则默认的ctor就会被调用,比如加入std::string
C/C++ code
#include <string>
class A
{
public:
int a;
char *ptr;
std::string test;
};

int main()
{
A a;
return 0;


}Assembly code[code]
int main()
99:   {
0040D680   push        ebp
0040D681   mov         ebp,esp
0040D683   sub         esp,5Ch
0040D686   push        ebx
0040D687   push        esi
0040D688   push        edi
0040D689   lea         edi,[ebp-5Ch]
0040D68C   mov         ecx,17h
0040D691   mov         eax,0CCCCCCCCh
0040D696   rep stos    dword ptr [edi]
100:      A a;
0040D698   lea         ecx,[ebp-18h];this指针
0040D69B   call        @ILT+35(A::A) (00401028);默认的ctor被调用
101:      return 0;
0040D6A0   mov         dword ptr [ebp-1Ch],0
0040D6A7   lea         ecx,[ebp-18h]
0040D6AA   call        @ILT+40(A::~A) (0040102d)
0040D6AF   mov         eax,dword ptr [ebp-1Ch]
102:  }
0040D6B2   pop         edi
0040D6B3   pop         esi
0040D6B4   pop         ebx
0040D6B5   add         esp,5Ch
0040D6B8   cmp         ebp,esp
0040D6BA   call        __chkesp (00401170)
0040D6BF   mov         esp,ebp
0040D6C1   pop         ebp
0040D6C2   ret


[/code]
(2)有虚函数的存在,也将调用默认的ctor,比如:

C/C++ code
[code]class A
{
public:
int a;
char *ptr;
virtual void dd(){a=1;}
};

int main()
{
A a;
return 0;
}




相应的Assembly
Assembly code
100:      A a;
00401048   lea         ecx,[ebp-0Ch]
0040104B   call        @ILT+15(A::A) (00401014);这里面主要是将虚函数表首地址放入前面4个字节

可以总结得出:编译器总是在需要ctor的时候,而用户没提供ctor的时候,才产生默认的ctor。这么做可以产生最优的效率。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: