[Cpp] 面向對象程序設計 C++

 

初始化列表(包括成員對象初始化)html

  初始化列表 ( 推薦 ) :
  能夠初始化任何類型的數據, 不論是不是普通類型仍是對象,都建議用.
  再也不須要在構造器中賦值了, 並且初始化列表比構造函數要早執行.
  成員初始化次序取決於成員在類中的聲明次序.

  當類成員有其它對象時,構造器內給對象賦值會觸發成員對象的默認構造函數(無參數的),若是成員對象沒有默認構造函數編譯報錯.
  因此有成員變量爲對象這種場景下,要用 initializer list.git

Source:https://github.com/farwish/unix-lab/blob/master/cpp/Initializer_list.ccgithub

 

繼承安全

  複用的一種方式,還有上面介紹過的 "對象組合"(成員變量爲其餘對象)ide

  私有屬性只能由父類本身訪問;受保護的屬性能夠由子類訪問,別人都沒法訪問.函數

  當實例化子類時,會先調用父類的構造函數,當父類沒有默認構造函數時又沒有初始化本身的構造函數時,編譯報相似 "no matching function AA::AA( )",因此在子類中只能用 initializer list 對父類成員初始化.ui

  析構的調用次序則反過來,先子類後父類.this

Source: https://github.com/farwish/unix-lab/blob/master/cpp/Extends.ccspa

 

函數重載(Function overload)和默認參數(Default argument)設計

  同名函數經過擁有不一樣的參數表實現重載. void print( );   void print ( int i )

  默認參數是在頭文件中給原型的默認參數值,惟一的好處是某些狀況下少打字;可是在調用時容易形成閱讀困難,另外也不安全,若是咱們不 include 頭文件而是本身寫一個函數聲明,把默認參數值設爲其它的,那麼就和設計者的意圖不同。因此建議不使用 Default argument 如 void f (int i , int j = 10); 

  

內聯函數(Inline functions)

  當函數前面有 inline 時,它就是一個 declaration,而再也不是 defination,所以不須要擔憂重複定義的問題。

  內聯函數的 body 放在頭文件裏就能夠了,不須要 .cpp 文件,和傳統的一個 .h 對應一個 .cpp 不一樣。

  由於內聯函數有類型檢查,所以比作一樣事情的宏要好。 

  ( 使用場合:函數只有2~3行的,須要重複調用的;不適合的:函數比較大,遞歸 )

  成員函數在 class 聲明時若是給出了 body,那麼這些都是 inline 函數,只要有一個頭文件就夠了。

  另外一種寫法是保持 class 聲明乾淨,而爲單獨實現的成員函數前面加 inline.

Source: https://github.com/farwish/unix-lab/blob/master/cpp/Inline.h

Source: https://github.com/farwish/unix-lab/blob/master/cpp/Inline_main.cc

 

const; 不可修改的對象(對象成員)

成員函數 const 的用法:

  在聲明和定義的地方要一塊兒用. 

    int getData( ) const;

    int getData( ) const { return data }

  不修改數據的成員函數應該被定義爲 const.

  若是類有 const 成員變量 或者 實例一個 const 對象,那麼必定要在 initialize list 裏面初始化變量,不然編譯沒法經過,由於後面沒法修改它 (成員變量)。

  func( ) { } 和 func( ) const { } 是不同的,它們構成重載( overload ),由於它們至關因而 func( A* this ) 與 func( const A* this ),參數表不同。

Source: https://github.com/farwish/unix-lab/blob/master/cpp/Const_class.cc

 

引用(C++數據類型)

  char c; char* p = &c; char& r = c;

  本地變量或全局變量,必須有初始值,type& name = 'name'  

    int x = 3;

    int& y = x;            # 賦初值

    const int& z = x;  # z 不能作左值,可是能夠經過修改 x 來修改 z

  做爲參數和成員變量時,能夠沒有初始值,由於它們會在構造對象時被調用者初始化,type& name;

    void f ( int& x )

    f ( y );      # 在函數調用時初始化

  指針和引用的區別:

    引用不能爲 null.              指針能夠爲 null.

    引用依賴另外一個變量,是一個變量的別名.  指針獨立於已存在的對象.

    引用不能指向一個新的地址.        指針能夠更改指向不一樣的地址.

    cpp內存模型的複雜性體如今:三個地方放對象(堆,棧,全局數據區),訪問對象的方式(變量放對象,指針訪問,引用訪問)。

Source: https://github.com/farwish/unix-lab/blob/master/cpp/Reference.cc

 

引用再研究

  引用做爲類的成員時,聲明時沒有辦法給初始值,由於它須要和另一個變量捆綁在一塊兒,做爲別名;因此必須在構造函數的 initializer list 裏初始化。

  函數能夠返回一個引用,但不能引用本地變量。

  參數前的 const 的引用,const 保證不被修改,引用使傳參高效,好處是函數中不用使用 * 號。 

  參數傳引用,這說明參數是一個能夠作左值的東西,傳參不能使用變量非const的表達式。

    void func(int &); func(i * 3); // error:invalid initialization of non-const reference of type 'int&' from a temporary of type 'int'   error: in passing argument 1 of 'void f (int &)'

    void func(const int&); int i = 3; func(i * 3); // 區別僅在於參數是const的,正確輸出9

  不能對函數返回的對象作左值,編譯會報錯,error: using temporary as lvalue [-fpermissive]。

Source: https://github.com/farwish/unix-lab/blob/master/cpp/Reference_2.cc

 

向上造型(Upcasting)

  子類的對象當作父類的對象來看,叫作向上造型,由於通常習慣把父類畫在上面;Upcasting 必定是安全的,最多子類擁有的被無視。

  父類的對象當作子類的對象看,叫作向下造型,Downcasting 有風險,由於父類不必定擁有子類的東西。

  類型轉換和造型的區別,類型轉換原來的值轉換完就變了,而造型數據沒變,子類的對象仍是子類的對象,只是看待的眼光不同。

  Persion John('JOHN');

  Animal* p = &John;  // Upcast,  由於Person是Animal的一種, 但反過來就是 Downcast

  Animal& q = John;   // Upcast

 

多態性(polymorphism):Upcast 和 Dynamic binding 兩個條件構成多態性

  Upcast: 把派生類當作基類使用。

  Dynamic binding: 調用對象的函數。

    (Static binding: 調用代碼寫明的函數)

/**
 * 通用函數,對任何 Shape 和其子類都通用.
 *
 * 動態綁定,調用的 render 在運行時決定:
 *  p 有一個靜態類型和動態類型,若是 p 的 render 函數是 virtual 的,那麼是動態綁定,不是 virtual 則是靜態綁定。
 *  因此動態綁定仍是靜態綁定取決於 render 函數,而不是對象 p;若是咱們調用的是 move 函數,那麼就是靜態綁定。
 */
void render(Shape* p)
{
    p->render(); }

Source: https://github.com/farwish/unix-lab/blob/master/cpp/Polymorphism.cc

 

虛 析構函數

  Shape的析構不是 virtual 時,默認是靜態綁定,delete p 時,只有 Shape 的析構會被調用,Ellipse 的不會調用。

  Shape的析構是 virtual 時,表示動態綁定,delete p 時,會先調子類的析構,在調父類的析構。

    Shape* p = new Ellipse(100.0, 110.0);
    delete p;

  其它 OOP 語言默認就是 virtual 的,也就是動態綁定的,而C++默認是靜態綁定的,動態綁定須要手動加 virtual。

  若是一個類裏有一個 virtual 函數,它的析構函數就必須是 virtual 的。

  若是父類和子類有名字相同、參數表相同的 virtual 函數,那麼子類成員函數就對父類構成了重寫/覆蓋。

  子類成員函數中調用父類的同名函數用 Base::func( ) 的方式。

  父類裏有兩個 virtual 的重載(overload)函數,那麼子類裏也要實現兩個 overloaded 的函數,不然另外一個函數會發生 name hidden,只有 C++ 會發生函數的隱藏。

Source: https://github.com/farwish/unix-lab/blob/master/cpp/Polymorphism.cc

 

拷貝構造

  拷貝構造的惟一形式:T::T(const T&)

  拷貝構造何時被調用?
    1.用對象進行初始化時,Person p = p1 或 Person p(p1),這兩種寫法相同,注意它不是 assignment 而是 initialization (由於變量前有類型)。
    2.調用一個函數,函數的參數是一個對象時,void func(Person p);
    3.用返回對象的函數返回值進行初始化。

  Construction vs Assignment
  每一個對象只能構造一次,每一個對象應該被析構一次,
  對象一旦被構造,它能夠是被賦值的目標,前頭有類型就是 initialization,沒有類型就是 assignment.

  Copy constructor guidelines
  寫一個類就先寫三個函數 default constructor, virtual distructor, copy constructor。
  若是確實不須要拷貝構造,那麼就聲明爲私有,不建議這麼作,限制了不少事不能作。

Source: https://github.com/farwish/unix-lab/blob/master/cpp/Copy_constructor.cc

 

靜態對象

  static兩個基本的含義:

    靜態存儲,本地變量是static,這個本地變量具備持久存儲(事實上static的本地變量就是全局變量)。

    名字可見性,全局變量、函數的static,那麼這個全局變量、函數只在當前文件中可用。

  static 在 C++ 中的使用:

    靜態本地變量 - 持久存儲。

    靜態成員變量 - 全部對象間共享。

    靜態成員函數 - 全部對象間共享,它只能訪問靜態成員變量。

    對象是靜態的 - 除了遵照兩個基本法則(存儲、可見性),保證只構造析構一次。

  靜態初始化的依賴

    多個 cpp 文件都有本身的全局變量的狀況,沒人保證初始化順序前後;

    若是一個變量的初始化依賴另外一個變量的值做爲參數,那麼須要先初始化那另外一個變量,可是跨文件的初始化是不存在的。因此解決方案是,1. 別這麼幹。 2. 邏輯上許可的話,把全部有依賴的全局變量放到一個地方。

Source:https://github.com/farwish/unix-lab/blob/master/cpp/Static_members.cc

 

運算符重載(Overloading Operators) - 基本規則

  運算符容許經過本身定義 function 來重載。

  

  只有已存在的運算符能夠被重載,不能對類和枚舉重載,必須保持操做數個數(如加法須要兩個操做數),必須保證優先級。

  使用 operator 關鍵字做爲函數名字,如重載 * 號,就是 operator * (...)

  能夠做爲成員函數,const String String::operator + (const String& that); 須要兩個操做數,由於有默認參數 this 做爲第一個,因此再只須要一個。

  能夠是全局函數,const String operator + (const String& r, const String& l); 這時參數表須要兩個參數。

  Integer x(1), y(5), z;

  x + y; ====> x.operator+(y);  這裏的 x 就是receiver,receiver 決定 operator 用哪一個。

  z = x + y; 能夠。

  z = x + 3; 能夠。

  z = 3 + y;  編譯通不過。

  Tips:作成成員函數仍是函數?

    單目的運算符應該作成是成員的,但非強制。

    = ( ) [] -> ->* 必須是成員的。

    賦值運算符應該作成是成員的。

    全部其它二元操做符做爲非成員的。

  原型

  參數傳遞:若是是隻讀的,那麼傳 const 的「引用」,不修改算子的成員函數加 const,全局函數可能兩個都加或者又一個不加 const。

  返回值:1.決定了是對本身進行了修改仍是返回了新對象; 2.製造出的新對象是否是能夠作左值;

  運算符原型

  

 

  

Link:http://www.cnblogs.com/farwish/p/8099721.html

相關文章
相關標籤/搜索