1. 程式人生 > >類的自動轉化和強制型別轉換(C++)

類的自動轉化和強制型別轉換(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》