目的碼
旨在弄懂下面的程式碼,明確變數a1,a2,a3在建立時編譯器究竟幹了那些事:
#include<iostream>
using namespace std;
class A{
public:
int x;
A() {cout<<"A()"<<endl;}
A(int i) : x(i){cout<<"A(int i)"<<endl;}
A(const A &ra) : x(ra.x)
{cout<<"A(const A&)"<<endl;}
void operator=(const A&){cout<<"operator="<<endl;}
};
int main(){
cout<<"the assignment of a1 : "<<endl;
A a1;
a1 = 2;
cout<<"the copy initialization of a2 : "<<endl;
A a2 = 2;
cout<<"the direct initialization of a3 :"<<endl;
A a3(a1);
return 0;
}
執行結果:
建構函式定義的隱式型別轉換
任何只接受一個引數的建構函式,都隱式地定義了由該引數向該型別的隱式型別轉換
如A(int i)
定義了一個由int向A的隱式型別轉換
所以,在任何使用A物件的地方,可以用一個int代替,此時,int會轉換為一個A型別臨時變數
如對a1變數的賦值操作:
A a1; //宣告a1,a1被預設初始化
a1 = 2; //2轉換為A型別的臨時變數,對a1進行賦值操作
對於隱式型別轉換,需要注意兩點:
-
隱式型別轉換隻允許一步轉換
class B{ public: string B_s; B() = default; B(string s) : B_s(s){}; }; int main(){ B b1,b2; //錯誤:char*->string->B,進行了兩步轉換 b1 = "hello"; b2 = string("hello"); return 0; }
-
接受隱式型別轉換得到的物件的函式,引數傳遞方式必須是const引用傳遞
因為c++中,一般不修改臨時物件,所以臨時物件只能傳遞給const引用。
分析a1
A a1
:
a1進行預設初始化,呼叫預設建構函式A()
a1 = 2
- 字面量2隱式轉換為A型別的臨時物件
- 該臨時物件通過拷貝運算子
operator=
拷貝給a1 - 因為是臨時物件,所以
operator=
必須接受const引用,否則造成編譯錯誤
分析a2
A a2 = 2
- 字面量2隱式轉換為A型別的臨時物件
- 用臨時物件來拷貝初始化a2,呼叫拷貝建構函式
A(const A&)
,相當於A a2(A(2))
- 因為是臨時物件,所以拷貝建構函式
A(const A&)
必須接受const引用,否則造成編譯錯誤
特別注意
編譯器會將A a2(A(2))
優化為A a2(2)
所以程式輸出“A(int i)”,而不是“A(const A&)”
但是底層仍然呼叫了A(const A&),所以如果把A(const A&)改為A(A&),會造成編譯錯誤 error: cannot bind non-const lvalue reference of type 'A&' to an rvalue of type 'A'
這提示我們,在編寫c++程式時,如果不改變物件的值,那麼習慣性地採用const引用會避免許多難解的編譯錯誤
分析a3
用a1直接初始化a3,呼叫A(const A&)