## 一.轉換函數的類型
1. 轉換構造函數:從其他類型到類類型 `T`
2. 類型轉換函數:從類類型 `T` 到其他類型
## 二.轉換構造函數
### 1.工作
它的主要工作是構造對象,順帶完成類型轉換工作。
### 2.調用方式
**既可以被編譯器自動調用,也可以被用戶強制調用。**
### 3.定義和聲明
+ 轉換構造函數是構造函數中的一種,其定義和聲明與構造函數是一樣的。
+ 轉換構造函數在調用時只會傳入一個參數,因此應該保證 **轉換構造函數至少有一個形參,且最多只有一個形參沒有默認參數** 。
#### 例1
下面是一個帶有轉換構造函數的類 `Complex` 。
```c++
class Complex
{
private:
double real_;
double imaginary_;
public:
Complex() : real_(0), imaginary_(0)
{
}
//轉換構造函數
Complex(double real) : real_(real), imaginary_(0)
{
}
Complex(double real, double imaginary) : real_(real), imaginary_(imaginary)
{
}
Complex &set_real(double new_val)
{
this->real_ = new_val;
return *this;
}
Complex &set_imaginary(double new_val)
{
this->imaginary_ = new_val;
return *this;
}
};
```
然后,就可以這么創建對象:
```c
Complex a,b = 6,c(6,5);
```
接下來,我們來簡化構造函數:
```c++
class Complex
{
private:
double real_;
double imaginary_;
public:
Complex() : real_(0), imaginary_(0)
{
}
//既是轉換構造函數,也是普通構造函數
Complex(double real, double imaginary = 0) : real_(real), imaginary_(imaginary)
{
}
Complex &set_real(double new_val)
{
this->real_ = new_val;
return *this;
}
Complex &set_imaginary(double new_val)
{
this->imaginary_ = new_val;
return *this;
}
};
```
然后依然可以這么調用:
```c
Complex a,b = 6,c(6,5);
```
最后我們進一步簡化構造函數:
```c
class Complex
{
private:
double real_;
double imaginary_;
public:
//缺省構造函數,轉換構造函數,普通構造函數
Complex(double real = 0, double imaginary = 0) : real_(real), imaginary_(imaginary)
{
}
Complex &set_real(double new_val)
{
this->real_ = new_val;
return *this;
}
Complex &set_imaginary(double new_val)
{
this->imaginary_ = new_val;
return *this;
}
};
```
照樣可以這樣調用:
```c
Complex a,b = 6,c(6,5);
```
## 三.類型轉換函數
### 1.調用方式
**既可以被編譯器自動調用,也可以被用戶強制調用。**
### 2.定義和聲明
+ 類似于重載強制類型轉換運算符。
+ 只能 **在類內重載** 。因為轉換的主體是對象本身。
+ 沒有參數。因為調用時第一個參數為對象本身。
>[warning] 由于類型轉換函數只能被編譯器自動調用,它不允許添加默認參數。
+ 不可指定返回類型。但它有返回類型。
#### 例2
下面是一個帶有類型轉換函數的類 `Complex` 。
```c++
class Complex
{
private:
double real_;
double imaginary_;
public:
Complex() : real_(0), imaginary_(0) {}
Complex(double real, double imaginary) : real_(real), imaginary_(imaginary) {}
Complex &set_real(double new_val)
{
this->real_ = new_val;
return *this;
}
Complex &set_imaginary(double new_val)
{
this->imaginary_ = new_val;
return *this;
}
//類型轉換函數
operator double()
{
return this->real_;
}
};
```
然后就可以把 `Complex` 對象賦值給 `double` 型變量:
```c++
double d = Complex(6,5);
```
## 四.類型轉換與二義性
1. 不要令兩個類執行相同的類型轉換:如果 `A` 類有一個接受 `B` 類對象的構造函數,則不要在 `B` 類中再定義轉換目標是 `A` 類的類型轉換運算符。
2. 避免定義 `A` 與 `B` 的雙向轉換,特別是同時重載了雙目運算符。
3. 避免轉換目標是內置算術類型的類型轉換,特別是當你已經定義了一個轉換成算術類型的類型轉換時:
+ 不要再定義接受算術類型的重載運算符。如果用戶需要使用這樣的運算符,則類型轉換操作將轉換你的類型的對象,然后使用內置的運算符。
+ 不要定義轉換到多種算術類型的類型轉換。讓標準類型轉換完成向其他算術類型轉換的工作。
### 1.同一轉換起終點,多種轉換路徑
如果類中包含一個或多個類型轉換,則必須確保在類類型和目標類型之間只存在唯一一種轉換方式。 否則的話,我們編寫的代碼將很可能會具有二義性。
#### 例3
假設有以下類的聲明和函數:
```c++
struct B;
struct A {
int a;
A(){}
A(B &b){}
};
struct B {
int b;
B(){};
operator A(){};
};
A f(const A &a)
{
return a;
}
```
從上面代碼中可以看到,有兩種從B到A的轉換函數。因此,下面的代碼運行時會出現二義性:
```c++
B b;
f(b);//Ambiguous user-defined-conversion
```
### 2.同時定義多個內置類型與類類型的轉換
#### 例4
假設有以下類的聲明和函數:
```c++
struct A {
int a;
A(){}
operator double() {return 0;}
operator int() {return 0;}
};
void f(long double ld)
{
}
```
若執行下面代碼,則會出現二義性:
```c++
A a;
f(a);//Ambiguous user-defined-conversion
```
在對 `f` 的調用中,哪個參數類型都無法精確匹配 `long double` .然而只要后面再執行一次到 `long double` 的轉換即可。因此,在上面的兩個類型轉換中兩個都差不多,調用將產生二義性。

#### 例5
假設有以下類的聲明:
```c++
struct A
{
int a;
A(double a) {}
A(int a) {}
};
```
若執行下面代碼,則不會出現二義性:
```c++
short b = 3;
A a(b);
```
為什么呢?在對構造函數的調用中,哪個參數類型都無法精確匹配 `short`。但是把 `short` 提升成 `int` 的操作優于把 `short` 轉換成 `double` 的操作,因此匹配 `A(int a)` 。(注:類型提升優于其他算術轉換,詳情參考 *面向過程編程.函數.函數重載* )

盡管這個例子沒有引起二義性,但實際運用中這樣寫容易出現錯誤。
### 3.重載函數與轉換構造函數
#### 例6
假設有以下類的聲明和函數定義:
```c++
struct A
{
int a;
A(int i) {}
};
struct B
{
int b;
B(int i) {}
};
void overload(const A &a){}
void overload(const B &b){}
```
那么,當我們這樣調用 `overload` 時,出現了二義性:
```c++
overload(10);//ambiguous call to overloaded function
```
因為這樣調用 `overload` 函數時,有下面兩種匹配方式,而且差別也不大:

### 4.重載函數與用戶定義的轉換
當調用重載函數時,如果多個用戶定義的類型轉換都提供了可行匹配,則我們認為 **這些類型轉換優先級相同** 。 在這個過程中,我們不會考慮任何可能出現的標準類型轉換的級別。只有當重載函數能通過同名類型轉換函數得到匹配時,我們才會考慮其中出現的標準類型轉換。
#### 例7
假設有以下類的聲明和函數定義:
```c++
struct A
{
int a;
A(int i) {}
};
struct B
{
int b;
B(double i) {}
};
void overload(const A &a){}
void overload(const B &b){}
```
那么,當我們這樣調用 `overload` 時,依然會出現二義性:
```c++
overload(10);//ambiguous call to overloaded function
```
盡管在 `B` 中,匹配的函數中間需要將 `int` 轉換成 `double` ,但是編譯器仍認為 `A(int)` 和 `B(double)` 是差不多的匹配。

### 5.雙向轉換與二義性
#### 例8
假設有以下定義和聲明:
```c++
class Complex
{
private:
double real_;
double imaginary_;
public:
Complex() : real_(0), imaginary_(0) {}
//包括一個double 到 Complex 的轉換
Complex(double real, double imaginary = 0) : real_(real), imaginary_(imaginary) {}
//Complex 到 double 的轉換
operator double() const
{
return this->real_;
}
//加法運算友元函數
friend Complex operator+(const Complex& c1,const Complex& c2);
};
Complex operator+(const Complex& c1,const Complex& c2)
{
return Complex(c1.real_ + c2.real_,c1.imaginary_ + c2.imaginary_);
}
```
接下來用 `Complex` 定義兩個對象:
```c++
Complex c1(1,2),c2(3,4);
```
下面的運算沒有任何問題:
```c++
c1+c2;
```
但是接下來的運算便有了二義性:
```c++
c1+2.5;//2 overloads have similar conversions
```

- 閱讀說明
- 1.1 概述
- C++基礎
- 1.2 變量與常量
- 1.2.1 變量
- 1.2.2 字面值常量
- 字符型常量
- 數值型常量
- 1.2.3 cv限定符
- 1.3 作用域
- 1.3.1 標識符
- 1.3.2 *命名空間
- 1.3.3 作用域
- 1.3.4 可見性
- 1.4 數據類型
- 1.4.1 概述
- 1.4.2 處理類型
- 類型別名
- * auto說明符
- * decltype說明符
- 1.4.3 數組
- 1.4.4 指針
- 1.4.5 引用
- 1.5 表達式
- 1.5.1 概述
- 1.5.2 值的類別
- 1.5.3 *初始化
- 1.5.4 運算符
- 算術運算符
- 邏輯和關系運算符
- 賦值運算符
- 遞增遞減運算符
- 成員訪問運算符
- 位運算符
- 其他運算符
- 1.5.5 *常量表達式
- 1.5.6 類型轉換
- 第2章 面向過程編程
- 2.1 流程語句
- 2.1.1 條件語句
- 2.1.2 循環語句
- 2.1.3 跳轉語句
- 2.1.4 *異常處理
- 2.2 函數
- 2.2.1 概述
- 2.2.2 函數參數
- 2.2.3 內置函數
- 2.2.4 函數重載
- 2.2.5 * 匿名函數
- 2.3 存儲和生命期
- 2.3.1 生命周期與存儲區域
- 2.3.2 動態內存
- 2.4 *預處理命令
- 第3章 面向對象編程
- 3.1 概述
- 3.2 類和對象
- 3.3 成員
- 3.3.1 訪問限制
- 3.3.2 常成員
- 3.3.3 靜態成員
- 3.3.4 成員指針
- 3.3.5 this指針
- 3.4 特殊的成員函數
- 3.4.1 概述
- 3.4.2 構造函數
- 3.4.3 析構函數
- 3.4.4 拷貝語義
- 3.4.5 * 移動語義
- 3.5 友元
- 3.6 運算符重載與類型轉換
- 3.6.1 概述
- 3.6.2 重載方法
- 3.6.3 類型轉換
- 3.7 繼承與多態性
- 3.7.1 概述
- 3.7.2 派生類
- 3.7.3 子類型
- 3.7.4 虛基類
- 3.7.5 虛函數
- 3.7.6 抽象類
- 3.8 模板與泛型
- 3.8.1 概述
- 3.8.2 模板類型
- 3.8.3 *模板參數
- 3.8.4 *模板編譯
- 3.8.5 *模板推斷
- 3.8.6 *實例化與特例化
- 第4章 C++標準庫
- 4.1 概述
- 4.2 輸入輸出流
- 4.2.1 概述
- 4.2.2 *流的狀態
- 4.2.3 *常用流
- 4.2.4 *格式化I/O
- 4.2.5 *低級I/O
- 4.2.6 *隨機訪問
- 4.3 *C輸入輸出
- 4.3.1 *字符輸入輸出
- 4.3.2 *格式化輸入輸出
- 4.4 * 容器
- 4.4.1 * 概述
- 4.4.2 * 基本操作
- 4.4.3 * 順序容器
- 4.4.4 * 迭代器
- 4.4.5 * 容器適配器
- 4.5 * 泛型算法
- 4.6 * 內存管理
- 4.6.1 * 自動指針
- 4.7 * 其他設施