程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 用轉換操作符保護代碼的安全

用轉換操作符保護代碼的安全

編輯:關於VC++

摘要:不經意的對象轉換常常嚴重地危害代碼的安全。幸運的是,轉換操作符允許你根據 實際情況來啟用和禁用轉換,這有助於避免出現病態行為。

某些對象必須要被轉換成 低級形式,反之亦然。例如,使用 std::string 對象的程序員必須將之轉換為 char 指針, 請看下面例子:

string inf="mydata.txt";
ifstream infile (inf.c_str());// 必須要轉成 const char*

同樣,PSOIX 程序員需要將 <fstream> 對象轉換成文件描述符以便 使用本地系統調用。

如何在不危及代 碼安全性的前提下讓對象自動轉換到其底層類型呢?

使用轉換操作符和 explicit 構 造函數來創建具備雙接口的對象,從而避免病態行為轉換。

提出問題:

商業 和金融應用常常將幣值表示成對象,而不是原始的浮點類型。之所以要這樣做有幾個原因:

類型安全:人為錯誤更容易被發現;

可移植性:由於對用戶隱藏實現細 節,代碼具有更好的可移植性;

業務邏輯:類允許你強化了業務邏輯規則。例如 :美元(US dollar)類知道一美元是 100 美分(cents),而科威特第納爾(dinar)類知 道一第納爾是 1000 菲爾斯(fils)。這種差別將影響 I/O 格式。

下面是一個簡 化了的表示美國貨幣的類:

class USD
{
private:
  __int64 dollars; //或者 long long, 依賴編譯器
  int cents;
public:
   USD(__int64 d=0, int c=0) :
   dollars(d), cents(c) {}
   friend bool operator==(const USD& d1, const USD& d2);
   //...other overloaded operators and functions
};  

唉,許多數學函數如:pow() 和 sqrt()都只認浮點變量。為了克服這個問題人們總是去重載關系操作符和算子。然而,你 會發現這將帶來大量無謂的編碼,測試和維護工作。你想要的只不過是一個雙接口:在適當 的上下文中,USD 類對象除了應該提供安全的自動的到基本類型的轉換外,它還應該提供上 述所列的優點。

使用轉換操作符:

轉換操作符在適當的上下文中將其對象自 動轉換成不同的類型。在類 USD 中,最自然的轉換是到浮點的轉換:

USD payment(203, 67);

此時,你想將支付對象轉換為浮點值 203.76。

轉換操 作符沒有返回值(從操作符的名字上判斷),也不帶任何參數:

class USD
{
public:
  operator double() //conversion operator
  {
    double temp=(dollars*100)+cents;
    return temp/100;
   }
};  

讓我們看看它是如何工作的。假設你想增加 5% 的支付:

double res=payment*1.05; //res=210.70

這樣能工作得很好,因為進 行乘法之前轉換操作符自動將支付轉換為值 200.67。但是,在 USD 類的設計上有一個嚴重 的缺點。請看下面的代碼:

payment=payment*1.05; // 很糟

現在,支 付等於 210.00,這當然不是所期望的結果。讓我們來看看為什麼。右邊子表達式 payment*1.05 首先被求值。正如你所看到的,這一部分沒什麼問題,產生的結果也是正確的 。問題出在賦值上。編譯器進行賦值的表達式如下:

payment=USD(__int64 (200.67*1.05), 0);

乘法表達式的結果被暗中轉換為整型(因此丟失分數部分) ,然後被用作一個臨時 USD 對象的參數。這就是為什麼它會產生這樣一個令人為難的結果。

聲明 ‘explicit’構造函數:

為了解決這個問題,首先將構造函 數聲明為 explicit:

class USD
{
public:
  explicit USD (__int64 d=0, int c=0):
  dollars(d), cents(c){}
  ...

這 樣,只有 USD 對象的賦值才被接受:

payment=USD(payment*1.05); // 沒問題
payment=payment*1.05; // 編譯出錯

添加另一個構造函數:

其次 是添加另一個構造函數,該構造函數帶一個 double 類型的參數:

class USD
{
public:
  explicit USD(double val)
  {
     dollars=val; // 拷貝整型部分
    long double temp=(val-dollars)*100; // 吸取美分
    // 避免截斷 e.g., .6699 是 66 而不是 67
     cents=temp+0.5;
  }
};

在此,構造函數被聲明為 ‘explicit’,以避免不經意的賦值。為了讓支付增加 5%,使用如下形式:

payment = USD(payment*1.05);

現在,一切都沒有問題。混雜的構造 函數不經意的轉換被阻止,允許依賴轉換操作符到 double 的正當轉換。

以安全為重 :

程序員常常抱怨在類 std::string 中缺乏 const char * 轉換操作符。如果 std::string 具備這樣的操作符,你可能會寫:

string filename;
ifstream inf(filename);

代替丑陋的:

ifstream inf (filename.c_str());

不管怎樣,C++ 標准化委員會決定在 std::string 中不包 含此類轉換操作符,因為它在某些廣泛使用 char * 的庫中可能導致令人厭惡的 bug。在這 種情況下,標准委員會本著以安全為重的思路。與之對照,<fstream> 對象包含到 void * 類型的轉換操作符,你可以象下面這樣來使用它:

ifstream inf ("myfile");
if (inf) // 使用 void * 轉換操作符
// 使用此文件
else
// 失敗

當你設計自己的類時,應該考慮啟用/禁用哪種類型的 自動轉換。然後,通過定義適當的轉換操作符來啟用合法轉換,而通過聲明 explicit 構造 函數來阻止不合需要的轉換。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved