1:任何類如果沒有定義默認構造函數,就會合成出來?
2:合成出來的默認構造函數會明確設定類內每一個成員的值?
3:如何去證明呢?
如果你對1、2回答的都是不是,請跳過閱讀,以免浪費你的時間
對於問題1與2其實答案都是未必,C++標准是這樣寫的默認構造函數是由編譯器在需要的時候將其合成出來,這裡強調的是需要,而非必需,以程序示例:
#include<iostream>
#include<string>
using namespace std;
class A
{
public:
char *ptr;
//string str;
};
int main()
{
A b;
b.ptr=NULL;
return 0;
}
這個程序本身沒什麼好講的,能講的就是其匯編代碼,調試狀態下進入匯編代碼如下:
11: {
00401030 push ebp
00401031 mov ebp,esp
00401033 sub esp,44h
00401036 push ebx
00401037 push esi
00401038 push edi
00401039 lea edi,[ebp-44h]
0040103C mov ecx,11h
00401041 mov eax,0CCCCCCCCh
00401046 rep stos dword ptr [edi]
12: A b;
13: b.ptr=NULL;
00401048 mov dword ptr [ebp-4],0
14: return 0;
0040104F xor eax,eax
15: }
你能找到構造函數調用的地方嗎即A::A(),:),找不到吧,因為壓根就沒有構造函數,這個類就相當於一個整形變量(存儲上相似,用法上不同),其空間是堆棧ebp+4這裡的4個字節
將程序注釋中的
//string str;
去掉,再次進入匯編看看,代碼如下:
10: int main()
11: {
00401070 push ebp
00401071 mov ebp,esp
00401073 sub esp,58h
00401076 push ebx
00401077 push esi
00401078 push edi
00401079 lea edi,[ebp-58h]
0040107C mov ecx,16h
00401081 mov eax,0CCCCCCCCh
00401086 rep stos dword ptr [edi]
12: A b;
00401088 lea ecx,[ebp-14h]
0040108B call @ILT+15(A::A) (00401014)
13: b.ptr=NULL;
00401090 mov dword ptr [ebp-14h],0
14: return 0;
00401097 mov dword ptr [ebp-18h],0
0040109E lea ecx,[ebp-14h]
004010A1 call @ILT+30(A::~A) (00401023)
004010A6 mov eax,dword ptr [ebp-18h]
15: }
看看,我們的構造函數出現了吧A:A() :),為什麼會出現呢?
因為類裡面有一個類叫string,我們跟蹤發現string類定義在include/xstring裡,其形式如下:
typedef basic_string<char, char_traits<char>, allocator<char> >
string;
這是一個模板類,屬於STL范疇,不信你看看SGI STL源碼,在機會再講,繼教跟蹤,你會發現basic_string有一系列的構造函數,如下:
explicit basic_string(const _A& _Al = _A())
: allocator(_Al) {_Tidy(); }
basic_string(const _Myt& _X)
: allocator(_X.allocator)
{_Tidy(), assign(_X, 0, npos); }
basic_string(const _Myt& _X, size_type _P, size_type _M,
const _A& _Al = _A())
: allocator(_Al) {_Tidy(), assign(_X, _P, _M); }
basic_string(const _E *_S, size_type _N,
const _A& _Al = _A())
: allocator(_Al) {_Tidy(), assign(_S, _N); }
basic_string(const _E *_S, const _A& _Al = _A())
: allocator(_Al) {_Tidy(), assign(_S); }
basic_string(size_type _N, _E _C, const _A& _Al = _A())
: allocator(_Al) {_Tidy(), assign(_N, _C); }
其實重要的是第一個構造函數,因為此處調用就是它,給basic_string分配一個內存分配管理器:)
明白了吧,此處有是因為類裡面有一個string類的對象,別人屬於你,別人有自已的構造函數,需要為其賦一個初始值,你總不能不讓吧,於是編譯器就合成一個默認的構造函數,調用string裡的構造函數,為string對像置一個初始狀態
構造函數的確是不一定會有的,而且類裡的一些內置類型默認構造函數也不會給其設定一個默認值的,不信你再看看匯編,哪裡有對ptr的賦值:)
有四種情況編譯器會為合成默認構造函數
1:含有默認默認/構造函數的成員類對象
2:帶有默認/構造函數的基類對象
3: 含有虛函數的類
4:繼承虛基類的類
可以參考此書《深入理解C++對象模型》第二章 下載見 http://www.linuxidc.com/Linux/2012-03/56159.htm