c++隱式類型轉換存在的陷阱
目標程式碼
旨在弄懂下面的程式碼,明確變數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&)