類的自動轉化和強制型別轉換(C++)
可以將類定義成與基本型別或另一個類相關,使得從一種型別轉換為另一種型別是有意義的。
當一個類的建構函式中,有隻有接受一個引數的建構函式,這個建構函式就能作為轉換函式。
#pragma once #ifndef STONEWT_H_ #define STONEWT_H_ class Stonewt { private: enum { Lbs_per_stn = 14 }; int stone; double pds_left; double pounds; public: Stonewt(); Stonewt(double lbs); Stonewt(int stn, double lbs); virtual ~Stonewt(); void show_lbs() const; void show_stn() const; }; Stonewt::Stonewt() { } Stonewt::Stonewt(double lbs) { stone = int(lbs) / Lbs_per_stn; pds_left = int(lbs) % Lbs_per_stn+lbs-int(lbs); pounds = lbs; } Stonewt::Stonewt(int stn, double lbs) { stone = stn; pds_left = lbs; pounds = stn * Lbs_per_stn + lbs; } Stonewt::~Stonewt() { } #endif // !STONEWT_H_
其中,Stonewt(double lbs)這個建構函式可以作為轉換函式,即我們可以這樣:
Stonewt myCat;
myCat = 19.6;
即為
Stonewt myCat = Stonewt(19.6);
然而,如果給第二個引數提供預設值,它便可用於轉換int
Stonewt(int stn, double lbs = 0);
將建構函式用作自動型別轉換函式似乎是一項不錯的特性。然而,當程式設計師擁有更豐富的C++經驗時,將發現這種自動特性並非總是合乎需要的,因為這會導致意外的型別轉換。因此,C++新增了關鍵字explicit,用於關閉這種自動特性:
explicit Stonewt(double lbs);
這將關閉上述示例中介紹的隱式轉換,但仍然允許顯式轉換,即顯式強制轉換。
然而,當且僅當轉換不存在二義性時,才會進行這種二步轉換,也就是說,如果這個類還定義了建構函式Stonewt(long lbs),則編譯器將拒絕這些語句,可能指出,int可被轉換為long或double,因此呼叫存在二義性。
轉換函式:
既然可以將數字轉換成Stonewt物件,那麼可以做相反的轉換嗎?也就是說,是否可以將Stonewt物件轉換為double值,就像如下所示:
Stonewt wolfe(285.7);
double host = wolfe;
可以這樣做,但不是使用建構函式,建構函式只用於從某種型別轉換到類型別的轉換。要進行相反的轉換,必須使用特殊的C++運算子函式——轉換函式。
轉換函式是使用者定義的強制型別轉換,可以像使用強制型別轉換那樣使用它們。例如,如果定義了從Stonewt到double的轉換函式,就可以使用下面的轉換:
Stonewt wolfe(285.7);
double host = double(wolfe);
double thinker = (double)wolfe;
也可以讓編譯器來決定如何做:
Stonewt(20, 3);
double star = wells;
編譯器發現,右側是Stonewt型別,而左側是double型別,因此它將檢視程式設計師是否定義了與此匹配的轉換函式。(如果沒有找到這樣的定義,編譯器將生成錯誤訊息,指出無法將Stonewt賦給double。)
要轉換為typeName型別,需要使用這種形式的轉換函式:
operator typeName();
注意:
- 轉換函式必須是類方法;
- 轉換函式不能指定返回型別;
- 轉換函式不能有引數。
例如,轉換為double型別的函式的原型如下:
operator double();
typeName指出了要轉換成的型別,因此不需要指定返回型別。轉換函式是類方法意味著:它需要通過類物件來呼叫,從而告知函式要轉換的值。因此,函式不需要引數。
Stonewt.h
#pragma once
#ifndef STONEWT_H_
#define STONEWT_H_
#include<iostream>
class Stonewt
{
private:
enum { Lbs_per_stn = 14 };
int stone;
double pds_left;
double pounds;
public:
Stonewt();
Stonewt(double lbs);
Stonewt(int stn, double lbs);
virtual ~Stonewt();
void show_lbs() const;
void show_stn() const;
operator int() const;
operator double() const;
};
Stonewt::Stonewt()
{
}
Stonewt::Stonewt(double lbs)
{
stone = int(lbs) / Lbs_per_stn;
pds_left = int(lbs) % Lbs_per_stn+lbs-int(lbs);
pounds = lbs;
}
Stonewt::Stonewt(int stn, double lbs)
{
stone = stn;
pds_left = lbs;
pounds = stn * Lbs_per_stn + lbs;
}
Stonewt::~Stonewt()
{
}
void Stonewt::show_lbs() const {
std::cout << pounds <<" pounds\n";
}
void Stonewt::show_stn() const {
std::cout << stone << " stone, " << pds_left << " pounds\n";
}
Stonewt::operator int() const {
return int(pounds + 0.5);
}
Stonewt::operator double() const {
return pounds;
}
#endif // !STONEWT_H_
假設有以下程式碼:
Stone.cpp
#include<iostream>
#include"Stonewt.h"
int main() {
using std::cout;
Stonewt poppins(9, 2.8);
double p_wt = poppins;
cout << "Convert to double => ";
cout << "Poppins: " << p_wt << "pounds.\n";
cout << "Convert to int => ";
cout << "Poppins: " << (int)poppins << "pounds.\n";
return 0;
}
下面是程式清單的輸出:
如果將cout << "Poppins: " << (int)poppins << "pounds.\n"
的(int)去掉,因為在cout示例中,並沒有指出應轉換為int還是double型別,因此不可行,程式中出現了二義性。
但如果類只定義了double轉換函式,則編譯器將接受該語句。這是因為只有一種轉換可能,因此不存在二義性。
賦值的情況與此類似。對於當前的類宣告來說,編譯器將認為下面的語句有二義性而拒絕它:
long gone = poppins;
在C++中,int和double值都可以被賦給long變數,所以編譯器使用任意一個轉換函式都是合法的。
當類定義了兩種或更多的轉換時,仍可以用顯式強制型別轉換來指出要使用哪個轉換函式。
和轉換建構函式一樣,轉換函式也有其優缺點。提供執行自動、隱式轉換的函式所存在的問題是:在使用者不希望進行轉換時,轉換函式也可能進行轉換。因此,最好使用顯式轉換,而避免隱式轉換。在C++11中,可用explicit將轉換運算子宣告為顯式的。
宣告:以上整理自個人理解和Stephen Prata 著的《C++ Primer Plus》