第11章 使用類——型別轉換(二)將自定義型別轉換為內建型別

superbmc發表於2024-04-11

本文章是作者根據史蒂芬·普拉達所著的《C++ Primer Plus》而整理出的讀書筆記,如果您在瀏覽過程中發現了什麼錯誤,煩請告知。另外,此書由淺入深,非常適合有C語言基礎的人學習,感興趣的朋友可以自行閱讀此書籍。

上一節我們可以利用建構函式,將內建型別(double)轉換為自定義型別(Stonewt),那麼問題來了,可以將自定義型別轉換成內建型別嗎?

比如如下的語句:

Stonewt wolfe(285.7);
double host = wolfe;

建構函式只用於從某種型別到類型別的轉換。要進行相反的轉換,必須使用特殊的C++運算子函式——轉換函式。

轉換函式的格式如下:

operator typeName();

需注意以下幾點:

  • 轉換函式必須是類方法
  • 轉換函式不能指定返回型別
  • 轉換函式不能有引數

例如,轉換為double型別的函式的原型如下所示:

operator double();

typeName(此處為double)指出了要轉換成的型別,因此不需要指定返回型別。轉換函式是類方法意味著:它需要類物件來呼叫,從而告知函式要轉換的值。因此,函式不需要引數。

接下來,我們完善下Stonewt類,
類宣告如下:

//stonewt1.hpp
#ifndef _STONEWT1_HPP_
#define _STONEWT1_HPP_

class Stonewt
{
private:
  enum {Lbs_per_stn = 14};
  int stone;
  double pds_left;
  double pounds;

public:
  Stonewt(double lbs);
  Stonewt(int stn, double lbs);
  Stonewt();
  ~Stonewt();
  void show_lbs() const;
  void show_stn() const;
  operator double() const;
  operator int() const;
};
#endif

類方法實現如下:

//stonewt1.cpp

#include <iostream>
using std::cout;
#include "stonewt1.hpp"

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()
{
  stone = pds_left = pounds = 0;
}
Stonewt::~Stonewt()
{

}
void Stonewt::show_stn()const
{
  cout << stone << " stone," << pds_left << " pounds\n";
}
void Stonewt::show_lbs()const
{
  cout << pounds << " pounds\n";
}
Stonewt::operator int() const
{
  return int(pounds + 0.5);
}
Stonewt::operator double() const
{
  return pounds;
}

使用類:

//stone1.cpp
#include <iostream>
#include "stonewt1.hpp"

using std::cout;
int main()
{
    Stonewt poppings(9, 2.8);
    double p_wt = poppings;
    cout << "Convert to double => ";
    cout << "Popings: " << p_wt << " pounds.\n";
    cout << "Convert to int => ";
    cout << "Popings: " << int(poppings) << " pounds.\n";
    return 0;
}

程式執行結果如下:

Convert to double => Popings: 128.8 pounds.
Convert to int => Popings: 129 pounds.

假設我們省略了強制型別轉換,使用瞭如下的語句:

cout << "Popings: " << poppings << " pounds.\n";

編譯器就會報出二義性錯誤,因為它不知道該將popings換成double還是int。但當我們刪除掉operator int()轉換函式後,就可以編譯成功。

和轉換建構函式一樣,轉換函式也有其優缺點。提供自動、隱式轉換的函式所存在的問題是:在使用者不希望進行轉換時,轉換函式也可能進行轉換。

例如如下的程式碼:

int ar[20];
...
Stonewt temp(200);
...
int Temp = 1;
...
cout << ar[temp] << "!\n";

因為Stonewt定義了一個operator int(),因此Stonewt物件temp將被轉換為int 200,並用做數字索引,明顯會導致錯誤。

有兩種辦法防止這個問題:
1,使用explicit關鍵字修飾轉換函式,在需要的時候使用顯式強制轉換。

class Stonewt{
...
  explicit operator int() const;
  explicit operator double() const;
...
};

2,用一個功能相同的非轉換函式替換該轉換函式,僅在被現實呼叫時,該函式才會執行。
將:

Stonewt::operator int() {return int (pounds + 0.5);}

替換為:

int Stonewt::operator Stone_to_Int() {return int (pounds + 0.5);}

這樣,下面的語句將是非法的:

int plb = poppings;

但如果確實需要這種轉換,可以這樣做:

int plb = popings.Stone_to_Int();

應謹慎地使用隱式轉換函式。通常,最好選擇僅在被顯式呼叫時才會執行地函式。

總之,C++為類提供了下面的型別轉換:

  • 只有一個引數的類建構函式用於將型別與該引數相同的值轉換為類型別。例如,將int值賦給Stonewt物件時,接受int引數的Stonewt類建構函式將自動被呼叫。然而,在建構函式宣告中使用explicit可防止隱式轉換,而只允許顯式轉換。
  • 被稱為轉換函式的特殊類成員運算子函式,用於將類物件轉換為其他型別。轉換函式是類成員,沒有返回型別、沒有引數、名為operator typename(),其中typename是物件將被轉換成的型別。將類物件賦給typename變數或將其強制轉換為typename時,該轉換函式將自動被呼叫。

相關文章