c++ 成員函數與非成員函數的抉擇

來源:互聯網
上載者:User

1.盡量用類的非成員函數以及友元函數替換類的成員函數
例如一個類來類比人People 複製代碼 代碼如下:1 class People{
2 public:
3 ...
4 void Getup( );
5 void Washing( );
6 void eating( );
7 ...
8 }

其實上面三個動作是早上“起床”、“洗簌”、“吃飯”三個常見的動作,如果現在用一個函數來表示使用成員函數即為 複製代碼 代碼如下:1 class People
2 {
3 ...
4 void morningAction( )
5 {
6 Getup( );
7 Washing( );
8 eating( );
9 }
10 }

如果寫一個非成員函數即為 複製代碼 代碼如下:1 void moringAction(People& p)
2 {
3 p.Getup( );
4 p.Washing( );
5 p.eating( );
6 }

那麼是選擇類的成員函數還是類的非成員函數呢?

物件導向則要求是,將操作資料的函數與資料放在一起。但這不意味著要選擇成員函數。從封裝的角度看,成員函數的moringAction封裝性比非成員函數要低。如果某些東西被封裝,它就不再可見。越多東西被封裝,越少人可以看到它。所以使用非成員函數的類,封裝性較低。而越少人看到它,我們就有越大彈性去變化它,因為我們的改變僅僅直接影響看到改變的那些人事物。因此,越多東西被封裝,改變哪些東西能力越大。

在考慮對象內的資料。越少的代碼可以看到資料(訪問它),越多的資料可以被封裝,而我們也就越能自由改變對象資料。現在如果一個成員函數、非成員函數都能提供相同的機能,我們選擇非成員函數。

在說下面內容之前我們先談談C++中的類型轉換分顯示類型轉換和隱式類型轉換。

2.顯示類型轉換

C++有顯示類型轉換操作符分別為:static_cast,const_cast,dynamic_cast和reinterpret_cast

2.1static_cast
轉換功能與C風格類型轉換一樣,含義也一樣。通過使用static_cast可以使沒有繼承關係的類型進行轉換。但是要注意不能將內建類型轉化為自訂類型,或者將自訂類型轉化為內建類型,並且不能將cosnt類型去掉,但能將non-cosnt類型轉換為const類型。例如:
char a='a';

int b=static_cast<int>(a);
兩個類型之間的轉換,其實也是要自己實現支援,原理和內建類型之間轉換相似。 複製代碼 代碼如下:1 #include <iostream>
2 class Car;
3
4 class People
5 {
6 public:
7 People(int a,int h)
8 :age(a),height(h)
9 {}
10
11 inline int getAge() const
12 {
13 return age;
14 }
15
16 inline int getHeight() const
17 {
18 return height;
19 }
20
21 People & operator=(const Car& c);
22 private:
23 int age;
24 int height;
25 };
26
27 class Car
28 {
29 public:
30 Car(double c, double w)
31 :cost(c),weight(w)
32 {}
33
34 inline double getCost() const
35 {
36 return cost;
37 }
38
39 inline double getWeight() const
40 {
41 return weight;
42 }
43
44 Car & operator=(const People& c);
45 private:
46 double cost;
47 double weight;
48 };
49
50 People & People::operator=(const Car& c)
51 {
52 age = static_cast<int>(c.getCost());
53 height = static_cast<int>(c.getWeight());
54 return *this;
55 }
56
57 Car & Car::operator=(const People& c)
58 {
59 cost = static_cast<double>(c.getAge());
60 weight = static_cast<double>(c.getHeight());
61 return *this;
62 }
63
64 int main(int argc,char * argv[])
65 {
66 Car c(1000.87,287.65);
67 People p(20,66);
68 People p2(0,0);
69 Car c2(0.00,0.00);
70 p2=c;
71 c2=p;
72 std::cout<< "car'info: cost is " << c2.getCost() << ". weight is " << c2.getWeight() <<std::endl;
73 std::cout<< "people'info: age is " << p2.getAge() <<". height is " << p2.getHeight() <<std::endl;
74 return 0;
75 }

運行結果為
car'info: cost is 20. weight is 66
people'info: age is 1000. height is 287

2.2const_cast
主要用來去掉const和volatileness屬性,大多數情況下是用來去掉const限制。

2.3dynamic_cast
它用於安全地沿著繼承關係向下進行類型轉換。一般使用dynamic_cast把指向基類指標或者引用轉換成其衍生類別的指標或者引用,並且當轉換失敗時候,會返回null 指標。

2.4reinterprit_cast
該轉換最普通用途就是在函數指標類型之間進行轉換。 複製代碼 代碼如下:1 typedef void (*fun) ( ) //一個指向空函數的指標
2 fun funArray[10]; //含有10個函數指標的資料。
3 int function( ); //一個傳回值為int類型函數
4 funArray[0] = &function( ) //錯誤!類型不符
5 funArray[0] = reinterpret_cast<fun>(&function); //ok注意轉換函式指標的代碼是不可移植的。

一般應該避免去轉換函式指標類型。

3.使用非成員函數可以發生隱式轉換
C++是支援隱式類型轉換的,例如在做運算的時候,或者傳遞參數給函數的時候常常會發生隱式類型轉換。
假設你設計一個class用來表現有理數。其實令類支援隱式類型轉換是一個槽糕的決定。當然在建立數實值型別時就是例外。下面定義一個有理數類型: 複製代碼 代碼如下:1 class Rational {
2 public:
3 Rational( int numerator = 0,int denominator =1 );
4 int numerator( ) const;
5 int denominator ( ) const ;
6 private:
7 ...
8 }

有理數類型想當然支援算數運算,但是不確定是否聲明為成員函數或非成員函數或者是友元函數來實現它們。

首先是考慮成員函數寫法: 複製代碼 代碼如下:1 class Rational {
2 public:
3 ...
4 const Rational operator* (const Rational& rhs) const;
5 ...
6 }
7 Rational testOne(1,4);
8 Rational testTwo(1,1);
9 //做算術運算
10 Rational result = testOne * testTwo;
11 //與常量做運算
12 result = testOne * 2; //ok
13 //乘法滿足交換定律
14 result = 2 * testOne //error!!

那麼為什麼將常量提前就錯誤了呢?這裡我們換一種寫法 複製代碼 代碼如下:1 result = testOne.operator*(2); //ok
2 result =2.operator*(oneHalf); //error

這裡發生了什嗎?其實在第一行式子裡發生了所謂隱式類型轉換。哪裡發生了隱式類型轉換呢?看上面的代碼operator*函數參數是const Rational類型,而2是一個cosnt int類型,。編譯器發現有non-explicit型的單參數類為int類型的建構函式,可以造出Rational類型。所以編譯器那樣做了,發生了隱式類型轉換。所以第一個式子可以正常運行,但是第二個是沒有將Rational類型轉換為int類型的。
設計出上面兩個式子正常運行才算合理的運行。 複製代碼 代碼如下:1 class Rational{
2 ...
3 };
4 const Rational operator*(const Rational & lhs, const Rational & rhs)
5 {
6 return Rational(lhs.numerator() * rhs.numerator(),lhs.denominator() * rhs.denominator() );
7 }
8 Rational testOne(1, 4);
9 Rational result;
10 result = oneFourth *2;
11 result = 2 * oneFourth; 通過
12 }

按上面代碼設計成非成員函數,那麼在調用int類型整數的時候會發生隱式類型轉換。
operaotr* 是否應該稱為Rational class的一個友元函數呢?對本例子而言,完全沒有必要。

如果你需要為某個函數所有參數進行類型轉換,那麼這個函數必須是個非成員函數。

4.謹慎使用隱式類型轉換
我們對一些類型隱式轉換無能為力,因為它們是語言本身的特性。不過當編寫自訂類時,我們可以選擇是否提供函數讓編譯器進行隱式類型轉換。
有兩種函數允許編譯器進行隱式類型轉換:單參數建構函式與隱式類型轉換運算子。 複製代碼 代碼如下:1 public Name{
2 public:
3 Name(const string& s); //轉換string到Name
4
5 ...
6 };
7
8 class Rational { //有理數類
9 public:
10 //轉換從int到有理數類
11 Rational(int numerator=0,int denominatior =1);
12 ...
13 }

C++是支援隱式類型轉換的,例如在做運算的時候,或者傳遞參數給函數的時候常常會發生隱式類型轉換。有兩種函數允許編譯器進行隱式轉換。單參數建構函式和隱式類型轉換運算子。

也許前面說道了一些隱式類型轉換帶來的方便之處,下面說說一些麻煩之處。 複製代碼 代碼如下:1 template<class T>
2 class Array{
3 Array(int lowBound,int highBound);
4 Array(int size);
5 T& operator[](int index);
6 ...
7 };
8 bool oerpator==(const Array<int>& lhs,const Array<int>& rhs);
9 Array<int> a(10);
10 Array<int> b(10);
11 ...
12 for(int i=0;i < 10; ++i)
13 if(a == b[i]) {
14 ... }
15 else
16 {
17 ...
18 }

如果這裡不小心將數組a的下標忘記寫了,這裡編譯器應該報出警告資訊,但是其實是沒有的。因為編譯器將a看成Array<int>類型,b為 int,根據上面的經驗,編譯器看到一個non-explicit單參數建構函式其參數類型為int,而且operator需要一個Array<int>類型,那麼編譯器就這樣做了,發生了隱式類型轉換。相信如果真的發生這樣,會很麻煩。
怎麼樣避免呢?將建構函式聲明為explicit。避免隱式類型轉換。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.