[C++_G_CLASS] C++中類的建構函式__Jquery
來源:互聯網
上載者:User
首先看一個題目:
有一個類
class A
{
public:
A(); //c1
A(const A&); //c2
A(int i); //c3
A& operator=(const A&); //c4
};
問如下語句都調用哪些建構函式。
A a; //s1
A b=a; //s2
b=a; //s3
A c(); //s4
A b(a); //s5
A()=5; //s6
先講一下收穫:
1 一個類一般要提供3個建構函式,預設建構函式; 拷貝建構函式; 賦值函數(operator=重載)。
如果不定義這3個函數,編譯器也會產生預設的預設建構函式; 拷貝建構函式。( 賦值函數不太確定是否會產生預設的)。
2 拷貝建構函式和賦值函數,預設情況下,實行位拷貝。
類中可能含有指向某記憶體空間的指標,是定義這兩個函數的主要原因。你需要決定是否要求指標指向同一空間,或者再次分配記憶體。
3 使用預設建構函式時,必須不能使用括弧。
例如s1, s4。s1顯然是調用c1。s4呢。s4是聲明了一個傳回值為A的函數。
4 拷貝建構函式和賦值函數的區別,拷貝建構函式在建立對象的時候使用; 而賦值函數在賦值時引用(廢話:))
例如s2是調用c2。s3是調用c4。如果他們中任何一個沒定義,那麼相應的調用就會使用位拷貝.
可能s2中的文法令人和s3混淆,s5中的文法就相當清晰了。
5 如果類中定義了包含某類型T的參數的建構函式,則就有了T到類的類型轉換。
例如s6。5是int型的,類A有這樣的參數的建構函式,那麼先將5利用相應的建構函式( A(int) )轉化為A的對象,或者說是產生一個A的對象。等號左邊,顯然是調用預設建構函式產生一個類的對象。最後調用賦值函數,完成賦值。
6 還得談一下編譯器的區別。
A b()
{
A a;
return a;
}
A c=b();
在G++下,上面的代碼是不調用拷貝建構函式或預設建構函式的。
而使用VC下,是調用拷貝建構函式的。
我個人也是認為應該調用拷貝建構函式,首先是由局部變數產生返回的臨時變數,其次是由傳回值構造新的對象。需要調用兩次。但這裡編譯器G++做了最佳化。
如果在拷貝函數中改變了某個成員變數i的值,那麼上面的代碼是不會成功的。
比如i預設構造為0, 拷貝建構函式把它修改為2, 然後經過上面的代碼,在g++下c.i仍然是0而不是2。
實驗代碼如下:
平台
g++ version 4.0.3
Ubuntu 6.06 LTS - the Dapper Drake - released in June 2006
#include <cstdio>
//using namespace std;
/*
拷貝建構函式和=號重載的作用在於
防止位拷貝對類中指標,既而記憶體使用量的影響
解構函式要合理的釋放記憶體
=號重載在VC中是要求必須有傳回值的。
=號重載記得要判斷是否是自賦值。
=號重載最好是返回A&。這樣就提高了效率。
*/
class A
{
//除const static的變數可以在類體內賦值外,其餘變數均不可以賦值
int i;
public:
//預設建構函式
A()
{
i=0;
printf("A/n");
}
//拷貝建構函式
//拷貝建構函式的作用主要在於:如果類中含有指標,可以決定
// 該指標是否需要指向新的記憶體空間
// 否則,位拷貝的話只是完成簡單的複製。
A(const A&)
{
i=1;
printf("AConst/n");
}
//一般建構函式
A(int i)
{
i=2;
printf("AInt/n");
}
//=號重載
//傳回值如果是A,則如果有return語句,則會調用拷貝建構函式
// A& 則不會
A& operator=(const A&)
{
i=3;
printf("A=/n");
//
return *this;
/*
A a;
//此時也不調用拷貝建構函式, 編譯器最佳化了。
//答案應該是肯定的。VC編譯器中沒有對此作最佳化,仍然需要調用
return a;
*/
}
void speak()
{
printf("i = %d/n",i);
}
static void CSpeak()
{
printf("I'm the class!/n");
}
};
int main (int argc, char * argv[])
{
printf("Test 1: /n");
A a;
a.speak();
printf("/n");
printf("Test 2: /n");
//先調用A(int),利用參數5構造一個A對象
// 或說成將5強制類型轉換為A
//然後調用預設建構函式構造一個A對象
//再然後利用重載運算子賦值
A()=5;
printf("/n");
printf("Test 3: /n");
//不是聲明對象,而是聲明函數
A b();
b().speak();;
printf("/n");
printf("Test 4: /n");
A * c=new A();
printf("/n");
printf("Test 5: /n");
A d;
A e;
d=e;//如果不重載運算子,不會調用拷貝建構函式
d.speak();
e.speak();
printf("/n");
printf("Test 6: /n");
A f;
A g=f;//此時調用拷貝建構函式
printf("/n");
return 1;
}
A b()
{
/*
A a;
a.speak();
//不調用拷貝建構函式,VC編譯器中沒有對此作最佳化,仍然需要調用
return a;
*/
//調用拷貝建構函式
return (*(new A()));
}