首頁 > 軟體

一起聊聊C++中的特殊成員函數

2022-07-12 14:02:11

一:背景

在C#中要說類預設給我們定義的特殊成員函數,莫過於建構函式,但在 C++ 中這樣的特殊函數高達 6 種,有必要整合一下聊一聊。

二:特殊成員函數

1. 預設建構函式

和 C# 一樣,很多書中都說,如果使用者沒有定義 建構函式,那麼編譯器會給我們定義一個,參考下面的例子:

class Person {

public:
	string name;
	int age;
};

int main()
{
	Person person;
}

接下來觀察下組合程式碼,看下有沒有呼叫 預設建構函式 .

    Person person;
003E32EF  lea         ecx,[person]  
003E32F2  call        Person::Person (03E15EBh)

對於 C# 學習者來說有點懵哈,定義了就相當於new了, 哈哈,這是因為C++預設都是值型別哈,不過這裡有必要澄清一下,並不一定所有情況都會呼叫預設建構函式,因為C++的組合生成由各自 編譯器 來決定,如果 編譯器 覺得沒必要呼叫建構函式 那它就會把這一步省掉來加速效能,那什麼時候不會調呢? 參考如下程式碼。

class Person {

public:
	void show() {
		printf("show!");
	}
};

int main()
{
	Person person;
	person.show();
}

接下來看下組合程式碼。

    person.show();
00E73F4F  lea         ecx,[person]  
00E73F52  call        Person::show (0E713B6h) 

可以清楚的看到,這種情況下呼叫建構函式其實沒有必要,所以編譯器就乾脆省略了。

2. 解構函式

在C#中解構函式 是由CLR負責管理,在C++中沒有託管這個概念,所以預設只能是結束作用域之前,自動呼叫解構函式釋放,參考如下圖:

3. 賦值建構函式

剛才也說到了,在 C++ 中甭管是 class 還是 struct 預設都是值型別,既然是值型別就存在stack copy 的情況,在 C# 中也是因為重寫了 Equals 和 GetHashCode 來實現的值copy,接下來簡單看下程式碼:

class Person {

public:
	string name;
	int age;
};

int main()
{
	Person p1 = { "jack",20 };
	Person p2(p1);
}

再看下 Person p2(p1) 的組合程式碼。

    Person p2(p1);
000F80A2  lea         eax,[p1]  
000F80A5  push        eax  
000F80A6  lea         ecx,[p2]  
000F80A9  call        Person::Person (0F15C3h) 

從組合中可以看到呼叫了 Person::Person (0F15C3h) 函數,請注意,這個不是建構函式,而是 賦值建構函式, 可以偵錯下去看看哦。。。 截圖如下:

值得說一下的是,C++ 預設提供的賦值建構函式是淺copy,如果要實現深 copy 的話,或者有一些自定義的邏輯,建議自己實現一下。

class Person {

public:
	string name;
	int age;

public:
	Person(string name, int age) :name(name), age(age) {}
	Person(const Person& p) {
		name = p.name;
		age = p.age;
	}
};

int main()
{
	Person p1 = { "aaaaaaaaaaaaaaaaaaaaaaaaaaa",20 };
	Person p2(p1);
}

4. 賦值運運算元

在 C# 中值型別, 匿名型別, Record 都是重寫過 Equals 及 = 運運算元,所以可以在這些型別上用 =, 其實在 C++ 中也可以在 class 之間進行賦值,因為編譯器會幫我們重寫運運算元 = ,如何看出來呢?先看下程式碼:

class Person {

public:
	string name;
	int age;

public:
	Person(string name, int age) :name(name), age(age) {}
	Person(const Person& p) {
		name = p.name;
		age = p.age;
	}
};

int main()
{
	Person p1 = { "aaaaaaaaaaaaaaaaaaaaaaaaaaa",20 };
	Person p2 = { "bbbbbbbbbbbbbbbbbbbbbbbbbbb",22 };

	p2 = p1;
}

最後一句的 p2 = p1 之所以能成功是因為 = 被重寫了,參考組合程式碼。

    p2 = p1;
00FD967C  lea         eax,[p1]  
00FD967F  push        eax  
00FD9680  lea         ecx,[p2]  
00FD9683  call        Person::operator= (0FD161Dh) 

如果需要自定義,可以自己重寫。

class Person {

public:
	string name;
	int age;

public:
	Person(string name, int age) :name(name), age(age) {}
	Person(const Person& p) {
		name = p.name;
		age = p.age;
	}
	Person& operator = (const Person& p) {
		name = p.name;
		age = p.age;
		return *this;
	}
};

int main()
{
	Person p1 = { "aaaaaaaaaaaaaaaaaaaaaaaaaaa",20 };
	Person p2 = { "bbbbbbbbbbbbbbbbbbbbbbbbbbb",22 };

	p2 = p1;
}

到此這篇關於一起聊聊C++中的特殊成員函數的文章就介紹到這了,更多相關C++ 特殊成員函數內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com