程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++筆記:面向對象編程基礎

C++筆記:面向對象編程基礎

編輯:C++入門知識

面向對象編程基礎

面向對象編程基於三個基本概念:

  • 數據抽象-類
  • 繼承-基類/派生類
  • 動態綁定-基類的函數or派生類的函數

    面向對象編程概述

    面向對象編程的關鍵思想是多態性(polymorphism)。多態性派生於一個希臘單詞,意思是“許多形態”,之所以稱通過繼承而相關聯的類型為多態類型,是因為在許多情況下可以互換地使用派生類型或基類型的“許多形態”。在C++中,多態性僅用於通過繼承而相關聯的類型的引用或指針。
    在C++中,基類必須指出希望派生類重寫哪些函數,定義為virtual的函數是基類期待派生類重新定義的,基類希望派生類繼承的函數不能定義為虛函數。
    在C++中,通過基類的引用(或指針)調用虛函數時,發生動態綁定。引用(或指針)既可以指向基類對象也可以指向派生類對象,這一事實是動態綁定的關鍵。用引用(或指針)調用的虛函數在運行時確定,被調用的函數是引用(或指針)所指對象的實際類型所定義的。
    引用和指針的靜態類型與動態類型可以不同,這是 C++ 用以支持多態性的基石。


    基類

    • 保留字virtual的目的是啟用動態綁定。成員默認為非虛函數,對非虛函數的調用在編譯時確定。
    • 為了指明函數為虛函數,在其返回類型前面加上保留字virtual。除了構造函數之外,任意非static成員函數都可以是虛函數
    • 保留字只在類內部的成員函數聲明中出現,不能用在類定義體外部出現的函數定義上。
    • 基類通常應將派生類需要重定義的任意函數定義為虛函數。
    • 已定義的類才可以用作基類(這一規則暗示著不可能從類自身派生出一個類)

      訪問控制

      • 用戶代碼可以訪問類的public成員而不能訪問private成員,private成員只能由基類的成員和友元訪問。
      • 派生類對基類的public和private成員的訪問權限與程序中任意其他部分一樣:它可以訪問public成員不能訪問private成員

      • 有時作為基類的類具有一些成員,它希望允許派生類訪問但仍禁止其他用戶訪問這些成員。對於這樣的成員應使用受保護的訪問標號。protected成員可以被派生類對象訪問但不能被該類型的普通用戶訪問。

      • 派生類只能通過派生類對象訪問其基類的protected成員,派生類對其基類類型對象的protected成員沒有特殊訪問權限。[Code1]
      • 提供給派生類型的接口是protected成員和public成員的組合
      • C++語言不要求編譯器將對象的基類部分和派生部分和派生部分連續排列(內存空間)

        派生類

        • 定義派生類,使用類派生列表指定基類,類派生列表指定了一個或多個基類class classname: access-label base-class
        • 盡管不是必須這樣做,派生類一般會重定義所繼承的虛函數。派生類沒有重定義某個虛函數,則使用基類中定義
        • 派生類中虛函數的聲明必須與基類中的定義方式完全匹配, 但有一個例外:返回對基類型的引用(或指針)的虛函數。派生類中的虛函數可以返回基類函數所返回類型的派生類的引用(或指針)。
        • 一旦函數在基類中聲明為虛函數,它就一直為虛函數,派生類無法改變該函數為虛函數這一事實。派生類重定義虛函數時,可以使用virtual保留字,但不是必須這樣做。
        • 如果需要聲明(但並不實現)一個派生類,則聲明包含類名但不包含派生列表。[Code2]

          virtual與其他成員函數

          • C++中的函數調用默認不使用動態綁定,觸發動態綁定條件:
            • 只有指定為虛函數的成員函數才能進行動態綁定
            • 必須通過基類類型的引用或指針進行函數調用
            • 因為每個派生類對象都包含基類部分,所以可將基類類型的引用綁定到派生類對象的基類部分,也可以用指向基類的指針指向派生類對象。
            • 使用基類類型的引用或指針時,不知道指針或引用所綁定的對象的類型,編譯器都將它當作基類類型對象
            • 基類類型引用和指針的關鍵點在於靜態類型(在編譯時可知的引用類型或指針類型)和動態類型(指針或引用所綁定的對象的類型這是僅在運行時可知的)可能不同。
            • 非虛函數總是在編譯時根據調用該函數的對象、引用或指針的類型而確定
            • 覆蓋虛函數機制
              • 使用作用域操作符強制函數調用使用虛函數的特定版本[Code3]
              • 在派生類中虛函數調用基類版本時,必須顯式使用作用域操作符,如果派生類函數忽略了這樣做,則函數調用會在運行時確定並且將是一個自身調用,從而導致無窮遞歸
              • 虛函數與默認實參
                • 像其他任何函數一樣,虛函數也可以有默認實參,如果有用在給定調用中的默認實參值,該值將在編譯時確定
                • 如果一個調用省略了具有默認值的實參,則所用的值由調用該函數的類型定義,與對象的動態類型無關
                • 通過基類的引用或指針調用虛函數時,默認實參為在基類虛函數聲明中指定的值,如果通過派生類的指針或引用調用虛函數,則默認實參是在派生類的版本中聲明的值
                • 在同一虛函數的基類版本和派生類版本中使用不同的默認實參幾乎一定會引起麻煩。

                  為什麼會希望覆蓋虛函數機制?
                  最常見的理由是為了派生類虛函數調用基類中的版本。在這種情況下,基類版本可以完成繼承層次中所有類型的公共任務,而每個派生類型只添加自己的特殊工作。
                  例如,可以定義一個具有虛操作的Camera類層次。Camera類中的display函數可以顯示所有的公共信息,派生類(如PerspectiveCamera)可能既需要顯示公共信息又需要顯示自己的獨特信息。可以顯式調用Camera版本以顯示公共信息,而不是在PerspectiveCamera的display實現中復制Camera的操作。 在這種情況下,已經確切知道調用哪個實例,因此,不需要通過虛函數機制。



                  繼承

                  公用、私有和受保護的繼承

                  • 派生類可以定義零個或多個訪問標號,指定跟隨其後的成員的訪問級別。
                  • 對類所繼承的成員的訪問由基類中的成員訪問級別和派生類派生列表中使用的訪問標號共同控制
                  • 每個類控制它所定義的成員的訪問,派生類可以進一步限制但不能放松對所繼承的成員的訪問
                  • 派生類中的訪問標號決定派生類成員的訪問級別[Code4/5]
                    • 公用繼承,基類的public成員為派生類的public成員,基類的protected成員為派生類的protected成員。
                    • 受保護繼承,基類的public和protected成員在派生類中為protected成員。
                    • 私有繼承,基類的的所有成員在派生類中為private成員。
                    • 在派生類的成員函數內部訪問基類的成員,訪問級別取決於基類的類型(可訪問public/private)[Code4]
                    • 在派生類定義與實現的外部,即派生類的對象或者繼承自派生類的類,派生類成員訪問級別取決於派生類繼承基類的訪問標號[Code5]
                    • 派生類可以恢復繼承成員的訪問級別,但不能使訪問級別比基類中原來指定的更嚴格或更寬松。[Code6]
                    • class保留字定義的派生默認具有private繼承,而用struct保留字定義的類默認具有public繼承

                      友元關系和繼承

                      • 像其他類一樣,基類或派生類可以使其他類或函數成為友元,友元可以訪問類的private和protected數據。
                      • 友元關系不能繼承。基類的友元對派生類的成員沒有特殊訪問權限。如果基類被授予友元關系,則只有基類具有特殊訪問權限,該基類的派生類不能訪問授予友元關系的類。

                        靜態成員和繼承

                        • 如果基類定義static成員,則整個繼承層次中只有一個這樣的成員,無論從基類派生出多少個派生類,每個static成員只有一個實例
                        • static成員遵循常規訪問控制,如果成員在基類中為private,則派生類不能訪問它。
                        • 假定可以訪問成員,則既可以通過基類訪問static成員,也可以通過派生類訪問static成員。一般而言,既可以使用作用域操作符也可以使用點或箭頭成員訪問操作符


                          Code

                          Code1:派生類只能通過派生類對象訪問其基類的protected成員

                          /*假定 Bulk_item 定義了一個成員函數,接受一個 Bulk_item 對象的 引用和一個 Item_base 對象的引用,該函數可以訪問自己對象的 protected 成 員以及 Bulk_item 形參的 protected 成員,但是,它不能訪問 Item_base 形 參的 protected 成員。*/
                           void Bulk_item::memfcn(const Bulk_item &d, const Item_base &b)
                           {
                              // attempt to use protected member
                              double ret = price; // ok: uses this->price
                              ret = d.price; // ok: uses price from a Bulk_item object
                              ret = b.price; // error: no access to price from an Item_base
                          }
                          /*d.price 的使用正確,因為是通過 Bulk_item 類型對象引用 price;b.price 的 使用非法,因為對 Base_item 類型的對象沒有特殊訪問訪問權限。*/
                          

                          Code2:聲明(但並不實現)一個派生類

                          //error: a forward declaration must not include the derivation list class   Bulk_item : public Item_base;
                           //正確的前向聲明為:
                           //forward declarations of both derived and nonderived class
                           class Bulk_item;
                           class Item_base;
                          

                          Code3:覆蓋虛函數機制

                          Item_base *baseP = &derived;
                           //calls version from the base class regardless of the dynamic type of baseP
                           double d = baseP->Item_base::net_price(42);
                          

                          Code4:在派生類的成員函數內部訪問基類的成員,訪問級別取決於基類的類型

                          class Base {
                          public:
                              void basemem();
                          protected:
                              int i;
                          // ... 
                          };
                          
                          struct Public_derived : public Base {
                              int use_base() { return i; } // ok: derived classes can access i
                              // ...
                          };
                          struct Private_derived : private Base {
                              int use_base() { return i; } // ok: derived classes can access i
                          };

                          Code5:在派生類定義與實現的外部,即派生類的對象或者繼承自派生類的類,派生類成員訪問級別取決於派生類繼承基類的訪問標號

                          Base b;
                          Public_derived d1;
                          Private_derived d2;
                          b.basemem();   // ok: basemem is public
                          d1.basemem();  // ok: basemem is public in the derived class
                          d2.basemem();  // error: basemem is private in the derived class
                          
                          //派生訪問標號還控制來自非直接派生類的訪問:
                           struct Derived_from Private : public Private_derived {
                               // error: Base::i is private in Private_derived
                               int use_base() { return i; }
                           };
                           struct Derived_from_Public : public Public_derived {
                               // ok: Base::i remains protected in Public_derived
                               int use_base() { return i; }
                           };

                          Code6:派生類可以恢復繼承成員的訪問級別

                          class Base {
                           public:
                               std::size_t size() const { return n; }
                           protected:
                               std::size_t n;
                           };
                           class Derived : private Base { . . . };
                          
                           /*在這一繼承層次中,size 在 Base 中為 public,但在 Derived 中為 private。為了使 size 在 Derived 中成為 public,可以在 Derived 的 public 部分增加一個 using 聲明。如下這樣改變 Derived 的定義,可以使 size 成員 能夠被用戶訪問,並使 n 能夠被從 Derived 派生的類訪問:*/
                           class Derived : private Base {
                           public:
                           // maintain access levels for members related to the size of the object
                              using Base::size;
                           protected:
                               using Base::n;
                           // ... 
                          };
                          


                          From: http://blog.csdn.net/liufei_learning/article/details/21436035
                          						

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