天天看點

【Effective C++讀書筆記】篇一(導讀~條款01)

在師姐的建議下,買了《Effective C++》這本書,打算用一個月的零散時間看完,并用這個專欄來記錄我的學習心得。

導讀

#1

關鍵字 explicit 用來阻止構造函數的隐式轉換。

被聲明為explicit的構造函數通常比其non-explicit兄弟更受歡迎。因為它們禁止編譯器執行非預期(往往也不被期望)的類型轉換。除非我有一個好理由允許構造函數被用于隐式類型轉換,否則我會把它聲明為explicit。我鼓勵你遵循相同的政策。(部落客注:這裡說類型轉換容易讓人誤解,所謂轉換并不是我們了解的“類型轉換”,而是調用構造函數生成對象,是以說 explicit 禁止編譯器隐式調用構造函數,必須顯式調用構造函數)

該關鍵字常用在類的構造函數上。讓我們用例子來說明:

#include <iostream>

using namespace std;

class boy
{
	public:
		int age;
		boy(int _age):age(_age){}
};

class girl
{
	public:
		int age;
	explicit	girl(int _age):age(_age){}         //使用 explicit 阻止隐式轉換
};

int main()
{
	boy b1(1);					   // 顯式調用構造函數
	boy b2 = 2;					   // “隐式轉換”,即隐式調用了構造函數,相當于 b2(2)

	girl g1(1);					   // 顯式調用構造函數
	girl g2 = 2;                                       // 禁止隐式調用,編譯報錯,error: conversion from ‘int’ to non-scalar type ‘girl’

	return 0;
}
           

同時我們還需注意的是,當構造函數參數超過兩個時自動取消隐式調用構造函數,但有例外:構造函數有預設值,或者說是除了第一個參數,都有預設值。(注意:C++11标準重新定義了)

例如:

#include <iostream>

using namespace std;

class boy
{
	public:
		int age;
		int height;
		boy(int _age, int _height):age(_age), height(_height){}
};

int main()
{
	boy b = 2;
	return 0;
}
           

編譯報錯。如果改成:

#include <iostream>

using namespace std;

class boy
{
	public:
		int age;
		int height;
		boy(int _age = 0, int _height = 50):age(_age), height(_height){}
};

int main()
{
	boy b = 2;
	return 0;
}
           

此時就會有隐式調用,那麼唯一的參數就會賦給 age 。

一次意外,我使用了 explicit 修飾了拷貝構造函數,又學習到了。explict 還能夠修飾拷貝構造函數。參考:【C++】explicit關鍵字(一點心青)

#include <iostream>

using namespace std;

class boy
{
	public:
		explicit boy(int age, int height):age(age), height(height) {}
		explicit boy(const boy& b):age(b.age), height(b.height) {}
	private:
		int age;
		int height;
};

boy CopyBoy(const boy& b) {
	return boy(b);			// 傳回值需要隐式調用拷貝構造,編譯報錯
}

int main()
{
	boy b1(10, 160);
	boy b2 = b1;			// 隐式調用拷貝構造,編譯報錯,a.cc:18:11: error: no matching function for call to ‘boy::boy(boy&)’     boy b2 = b1;
	boy b3(b1);			// 顯式調用拷貝構造,通過編譯
	boy b4 = CopyBoy(b1);		// 隐式調用拷貝構造,編譯報錯
	return 0;
}
           

将拷貝構造函數聲明為explicit,則會阻止隐式拷貝構造函數的調用。拷貝構造函數被隐式調用主要發生在三處:

1.一個對象作為函數參數,以值傳遞的方式傳入函數體.

2.一個對象作為函數傳回值,以值傳遞的方式從函數傳回.

3.以AAA = xxx的方式建立對象AAA,xxx為與AAA為同類型的對象.

因而,将拷貝構造函數聲明成explicit并不常見,一般隻将 constructor 聲明為explicit,而copy constructor不要聲明為explicit。

說到這裡我們沒有看到隐式轉換有什麼壞處啊,那麼可以參考這裡,explicit 可以有效防止構造函數的隐式轉換帶來的錯誤或者誤解

Google認為:代碼的行為應該是明确的,不能偷偷摸摸的做一些事情給查找問題帶來困難。

https://github.com/lichuang/codedumpnote/blob/master/20160410-cpp-learn/20160410-cpp-learn.md

http://www.cnblogs.com/cutepig/archive/2009/01/14/1375917.html

Upate:C++11

C++11之前,沒有花括号初始化方法,當構造函數擁有多于1個的參數沒有預設值時,隐式調用将會被自動禁用。然而C++11引入了花括号清單初始化,使用 explicit 關鍵字來區分是否能夠隐式調用構造函數,并且與預設參數的個數無關。

可以參見

explicit specifier

Converting constructor

舉例如下

#include <iostream>

using namespace std;

class boy
{
	public:
		boy(int age, int height):age(age), height(height) {}
		boy(const boy& b):age(b.age), height(b.height) {}
	private:
		int age;
		int height;
};

int main()
{
	boy b1 = {10, 11};	// 隐式調用構造函數
	boy b2{10, 11};		// 顯式調用構造函數
	return 0;
}
           

編譯通過。

#include <iostream>

using namespace std;

class boy
{
	public:
		explicit boy(int age, int height):age(age), height(height) {}
		boy(const boy& b):age(b.age), height(b.height) {}
	private:
		int age;
		int height;
};

int main()
{
	boy b1 = {10, 11};
	boy b2{10, 11};
	return 0;
}
           

隐式調用報錯:

error: converting to ‘boy’ from initializer list would use explicit constructor ‘boy::boy(int, int)’
  boy b1 = {10, 11};
           

#2

"=" 等号即可以用作指派函數,也可用作拷貝構造函數。

幸運的是 “copy構造” 很容易和 “copy指派” 有所差別。如果一個新對象被定義,一定會有個構造函數被調用,不可能是指派操作。如果沒有新對象被定義,就不會有構造函數被調用,那麼當然就是指派操作被調用。

舉個例子: 

#include <iostream>

using namespace std;

class boy
{
	public:
		int age;
		int height;
		boy(int _age, int _height):age(_age), height(_height){}
		boy(const boy &b){age = 19; height = 170;}
		boy& operator=(const boy &b){age = 21; height = 180;}
		void display(){cout << age << " " << height << endl;}
};

int main()
{
	boy b1(20, 175);
	boy b2 = b1;                       //調用拷貝構造函數
	b2.display();
	b2 = b1;                           //調用指派函數
	b2.display();                      
	return 0;
}
           

完成導讀的内容,我們以條款1結束這節的内容。

條款01:視C++為一個語言聯邦

為更好的了解C++,我們其看做四個部分,每個部分有各自的規約:

1)C

2)Object-Oriented C++

3)Template C++

這是 C++ 的泛型程式設計(generic programming)部分,也是大多數程式員經驗最少的部分。

4)STL

請記住:C++ 高效程式設計守則是狀況而變化,取決于你使用 C++ 的哪一部分。

繼續閱讀