程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> Effective C++:條款37:絕不重新定義繼承而來的缺省參數值

Effective C++:條款37:絕不重新定義繼承而來的缺省參數值

編輯:C++入門知識

由於重新定義繼承而來的non-virtual函數是不正確的(見上一個條款),所以這個條款就將問題局限於:絕不重新定義繼承一個帶有缺省參數值的virtual函數。

(一)

virtual函數是動態綁定的,而缺省參數卻是靜態綁定。

對象的所謂靜態類型,是它在程序中被聲明時所采用的類型。

你可能會在“調用一個定義於derived class 內的virtual函數”的同時,卻使用了base class為它所指定的缺省參數值。

(二)

為什麼繼承而來的virtual函數的缺省參數值不能被重新定義呢?

其實原因也挺簡單:缺省參數是靜態綁定,而virtual函數是動態綁定. 所謂對象的靜態綁定也叫前期綁定,它是說該對象類型和行為在程序編譯期間就可以確定,例如:

class Shape{
     public:
              enum Color{RED,GREEN,BLUE};
              virtual void draw(Color color = RED)const = 0;
              ...
     };
     class Circle : public Shape{
     public:
              //哦歐! 竟然改變缺省參數值
              virtual void draw(Color color = GREEN)const{ ... }
     };
     class Rectangle : public Shape{
     public:
             //沒用指定參數類型,需要用戶去明確的指定其值
            //靜態綁定下不繼承基類的缺省值,若以指針或引用調用則不需要指定缺省值,因為動態綁定
            //繼承基類的參數缺省值
            virtual void draw(Color color)const{ ... }
     };
看一下下面幾個指針:

 Shape* ps;
 Shape* pc = new Circle;
 Shape* pr = new Rectangle;
這裡的ps,pc,pr不管它具體指向的是什麼對象,他們的靜態類型都是Shape*。而動態類型就是它們真正指向的對象的類型。故pc的動態類型為Circle*,而pr的動態類型為Rectangle*,ps由於沒有指向任何對象,所以此時沒有動態類型。


(三)看下面這個語句!

pc->draw();   //注意調用的是: Circle::draw(RED)
怎麼會調用Circle::draw(RED)呢!?為什麼不是Circle::draw(GREEN)?
原因:

(1)首先根據其調用語句用指針這一事實,我們就知道了其調用的版本應該是該指針的動態類型的函數版本,即Circle::draw,這個問題不大。

(2)下面我們來看它的傳值參數,前面我們提到缺省參數值是靜態綁定的,而pc的靜態類型是Shape*,所以該參數的傳入值是Shape的該函數版本的缺省值。

那為什麼C++堅持以這種乖張的方式來運作呢?答案在於運行期效率,如果缺省值也是動態綁定的,那麼編譯期就必須要有辦法在運行期為virtual函數決定適當的參數缺省值.如果這樣做的話,就要比目前實現的"在編譯期決定"的機制更慢而且更復雜,考慮到執行速度和實現上的簡易性,C++放棄了這樣的做法。


(四)解決方法!

現在,為了遵循本款約定卻同時提供缺省參數值給你的基類和父類,,代碼就這樣了:

class Shape{
     public:
              enum Color{RED,GREEN,BLUE};
              virtual void draw(Color color = RED)const = 0;
              ...
     };
     class Circle:public Shape{
     public:
              virtual void draw(Color color = RED)const {...}
     };
明顯的是代碼重復嘛!何況你要是想改變缺省值的話,必須要同時改變基類和子類函數的缺省值,一不小心,就會出現漏改或寫錯的情況,導致意想不到的錯誤出現.有沒用一種更方便的寫法呢?當然,你還記得NVI手法嗎?額..,(non-virtual interface),要是忘記的話,回過頭看看條款35,用這種手法的話,我們寫下代碼如下:

class Shape{
     public:
              enum Color{RED,GREEN,BLUE};
              void draw(Color color = RED) const{
                     ...
                     doDraw(color);
                     ...
              }
              ...
     private:
             virtual void doDraw(Color color) const = 0;  
     };
     class Circle:public Shape{
                   ...
     private:
              virtual void doDraw(Color color){ ... }
     };
由於draw是non-virtual而non-virtual絕對不會被重新改寫(條款36),所以color的缺省值總是為RED。

請記住:
(1)絕對不要重新定義一個繼承而來的缺省參數值,因為缺省參數值都是靜態綁定,而virtual函數-你唯一應該覆寫的東西-卻是動態綁定。








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