GeekBand學(xué)習(xí)筆記-第三周 關(guān)于面向?qū)ο蟮木幊膛c設(shè)計(jì)

寫在前面:感謝GeekBand提供這樣好的學(xué)習(xí)機(jī)會(huì),讓我在繁忙的工作之余可以學(xué)習(xí)鞏固c++知識(shí)。以下是邊學(xué)邊記的一些擴(kuò)展點(diǎn)。分享給大家。

關(guān)于UML:

推薦一個(gè)在線UML編輯器
Share 與 Composite 兩者區(qū)別

如果對(duì)象生命周期僅被構(gòu)造函數(shù)調(diào)用而不被析構(gòu)函數(shù)調(diào)用,那么是Share(僅僅是被擁有)關(guān)系。這個(gè)被擁有的對(duì)象可以單獨(dú)存活;
如果對(duì)象生命周期被構(gòu)造函數(shù)和析構(gòu)函數(shù)調(diào)用,那么是Composite(是對(duì)象的一部分)關(guān)系。這個(gè)被擁有的對(duì)象不可以單獨(dú)存活。可見,后者的是一種更加強(qiáng)烈的關(guān)系。

看這個(gè)例子

Aggregation: Share Vs Composite - 來自<Programming Game Ai by Example>
  • 【Share 關(guān)系】賽車的靜態(tài)網(wǎng)格(Mesh),可獨(dú)立存在,是隨賽車創(chuàng)建而創(chuàng)建的;這個(gè)例子適用于搭配類的委托(Delegation)
  • 【Composite關(guān)系】賽車的車殼(Chasis),不可獨(dú)立存在,不僅隨著賽車創(chuàng)建而創(chuàng)建,而且在賽車消失的時(shí)候,車殼也會(huì)消失。這個(gè)例子適用于搭配類的復(fù)合(Composite)
  • 【Generalization關(guān)系】用于體現(xiàn)類的繼承(Inheritance)關(guān)系


    Generalization - 來自<Programming Game Ai by Example>

    針對(duì)抽象類的表示 - 來自<Programming Game Ai by Example>

    作業(yè)中出現(xiàn)的抽象類

這個(gè)抽象類如果需要?jiǎng)?chuàng)建一個(gè)數(shù)組,并且10個(gè)Rectangle和10個(gè)Circle的話,那么可以用Shape的指針數(shù)組來實(shí)現(xiàn)

Shape** shapes = new Shape*[20];
指針的指針的知識(shí),如Shape ** shapes, 來自www.tutorialspoint.com

還有什么好的辦法,歡迎評(píng)論

這樣就可以分別調(diào)用子類的構(gòu)造函數(shù)了。
shapes[i] = new Rectangle(i, RInt(), RInt(),Point(RInt(), RInt()));
shapes[i] = new Circle(i, RInt(), Point(RInt(), RInt()));

但是,由于我們等下要?jiǎng)h除一些元素,所以我們不知道在刪除之后數(shù)組還剩多少個(gè)元素。所以需要自己來記錄數(shù)組的長度。請(qǐng)教了高人之后,高人推薦用結(jié)構(gòu)體。

struct MyStruct {
//http://www.cplusplus.com/doc/tutorial/structures/
    Shape** shapes = new Shape*[20];
    int Length;
} shapeArray;

這樣,我們就定義了一個(gè)叫MyStruct類型的結(jié)構(gòu)體,實(shí)例化出shapeArray。這個(gè)結(jié)構(gòu)體容器里有一個(gè)數(shù)組和一個(gè)記錄長度的變量。

我們可以這樣構(gòu)造
    shapeArray.shapes[i] = new Rectangle(i, RInt(), RInt(),Point(RInt(), RInt()));
    shapeArray.shapes[i] = new Circle(i, RInt(), Point(RInt(), RInt()));
刪除和查找的功能

這里需要注意幾個(gè)細(xì)節(jié)

  • 刪除完指針?biāo)笇?duì)象之后,指針需要置為空指針;
  • 不要直接刪除數(shù)組對(duì)象,而是把這個(gè)對(duì)象與最后一個(gè)元素交換后縮短數(shù)組長度;
  • 交換后記得 “i--”, 避免漏網(wǎng)之魚。
void DeleteShapes(int area, bool isClearAll)
{
    if (!isClearAll)
    { //Delete shapes whose area are less than the area
        Shape* shapePtr= nullptr;
        for (int i = 0; i < shapeArray.Length; i++)
        {
            if (shapeArray.shapes[i]->getArea() < area)
            {//Swap Position for sorting first
                shapePtr = shapeArray.shapes[i];
                shapeArray.shapes[i] = shapeArray.shapes[shapeArray.Length - 1];
                shapeArray.shapes[shapeArray.Length - 1] = shapePtr;
            //Then delete here.
                delete shapeArray.shapes[shapeArray.Length - 1];
                shapeArray.shapes[shapeArray.Length - 1] = nullptr;
                shapeArray.Length--;
                i--;
            }
        }
    }
    else
    {// Delete them all and reset
        for (int i = 0; i < shapeArray.Length - 1; i++)
        {
            delete shapeArray.shapes[i];
            shapeArray.shapes[i] = nullptr;
        }
        shapeArray.Length = 0;
    }
}

再說結(jié)構(gòu)體

簡單的概念不贅述,可以點(diǎn)這個(gè)小標(biāo)題鏈接查看。
這里就單補(bǔ)充一個(gè) 結(jié)構(gòu)體的指針

// pointers to structures
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

struct movies_t {
  string title;
  int year;
};

int main ()
{
  string mystr;

  movies_t amovie;
  movies_t * pmovie;
  pmovie = &amovie;

  cout << "Enter title: ";
  getline (cin, pmovie->title); // pmovie->title 等價(jià)于 (*pmovie).title
                                // amovie.ptitle 等價(jià)于 *(amovie.ptitle)
                                // <假設(shè)movies_t里有個(gè)string *ptitle指針>
  cout << "Enter year: ";
  getline (cin, mystr);
  (stringstream) mystr >> pmovie->year;

  cout << "\nYou have entered:\n";
  cout << pmovie->title;
  cout << " (" << pmovie->year << ")\n";

  return 0;
}

虛函數(shù)VS純虛函數(shù)

    class A  
    {  
    public:  
        virtual void foo()  
        {  
            cout<<"A::foo() is called"<<endl;  
        }  
    };  
    class B:public A  
    {  
    public:  
        void foo()  
        {  
            cout<<"B::foo() is called"<<endl;  
        }  
    };  
    int main(void)  
    {  
        A *a = new B();  
        a->foo();   // 在這里,a雖然是指向A的指針,但是被調(diào)用的函數(shù)(foo)卻是B的!  
        return 0;  
    }  

這個(gè)例子是虛函數(shù)的一個(gè)典型應(yīng)用,通過這個(gè)例子,也許你就對(duì)虛函數(shù)有了一些概念。它虛就虛在所謂“推遲聯(lián)編”或者“動(dòng)態(tài)聯(lián)編”上,一個(gè)類函數(shù)的調(diào)用并不是在編譯時(shí)刻被確定的,而是在運(yùn)行時(shí)刻被確定的。由于編寫代碼的時(shí)候并不能確定被調(diào)用的是基類的函數(shù)還是哪個(gè)派生類的函數(shù),所以被成為“虛”函數(shù)。

虛函數(shù)只能借助于 指針或者引用 來達(dá)到多態(tài)的效果。

C++純虛函數(shù)

一、定義
 純虛函數(shù)是在基類中聲明的虛函數(shù),它在基類中沒有定義,但要求任何派生類都要定義自己的實(shí)現(xiàn)方法。在基類中實(shí)現(xiàn)純虛函數(shù)的方法是在函數(shù)原型后加“=0”
 virtual void funtion1()=0
二、引入原因
  1、為了方便使用多態(tài)特性,我們常常需要在基類中定義虛擬函數(shù)。
 2、在很多情況下,基類本身生成對(duì)象是不合情理的。例如,動(dòng)物作為一個(gè)基類可以派生出老虎、孔雀等子類,但動(dòng)物本身生成對(duì)象明顯不合常理。
  為了解決上述問題,引入了純虛函數(shù)的概念,將函數(shù)定義為純虛函數(shù)(方法:virtual ReturnType Function()= 0;),則編譯器要求在派生類中必須予以重寫以實(shí)現(xiàn)多態(tài)性。同時(shí)含有純虛擬函數(shù)的類稱為抽象類,它不能生成對(duì)象。這樣就很好地解決了上述兩個(gè)問題。
聲明了純虛函數(shù)的類是一個(gè)抽象類。所以,用戶不能創(chuàng)建類的實(shí)例,只能創(chuàng)建它的派生類的實(shí)例。
純虛函數(shù)最顯著的特征是:它們必須在繼承類中重新聲明函數(shù)(不要后面的=0,否則該派生類也不能實(shí)例化),而且它們?cè)诔橄箢愔型鶝]有定義。
定義純虛函數(shù)的目的在于,使派生類僅僅只是繼承函數(shù)的接口。
純虛函數(shù)的意義,讓所有的類對(duì)象(主要是派生類對(duì)象)都可以執(zhí)行純虛函數(shù)的動(dòng)作,但類無法為純虛函數(shù)提供一個(gè)合理的缺省實(shí)現(xiàn)。所以類純虛函數(shù)的聲明就是在告訴子類的設(shè)計(jì)者,“你必須提供一個(gè)純虛函數(shù)的實(shí)現(xiàn),但我不知道你會(huì)怎樣實(shí)現(xiàn)它”。

抽象類的介紹
抽象類是一種特殊的類,它是為了抽象和設(shè)計(jì)的目的為建立的,它處于繼承層次結(jié)構(gòu)的較上層。
(1)抽象類的定義: 稱帶有純虛函數(shù)的類為抽象類。
(2)抽象類的作用:
抽象類的主要作用是將有關(guān)的操作作為結(jié)果接口組織在一個(gè)繼承層次結(jié)構(gòu)中,由它來為派生類提供一個(gè)公共的根,派生類將具體實(shí)現(xiàn)在其基類中作為接口的操作。所以派生類實(shí)際上刻畫了一組子類的操作接口的通用語義,這些語義也傳給子類,子類可以具體實(shí)現(xiàn)這些語義,也可以再將這些語義傳給自己的子類。
(3)使用抽象類時(shí)注意:
抽象類只能作為基類來使用,其純虛函數(shù)的實(shí)現(xiàn)由派生類給出。如果派生類中沒有重新定義純虛函數(shù),而只是繼承基類的純虛函數(shù),則這個(gè)派生類仍然還是一個(gè)抽象類。如果派生類中給出了基類純虛函數(shù)的實(shí)現(xiàn),則該派生類就不再是抽象類了,它是一個(gè)可以建立對(duì)象的具體的類。抽象類是不能定義對(duì)象的。

總結(jié):
1、純虛函數(shù)聲明如下: virtual void funtion1()=0; 純虛函數(shù)一定沒有定義,純虛函數(shù)用來規(guī)范派生類的行為,即接口。包含純虛函數(shù)的類是抽象類,抽象類不能定義實(shí)例,但可以聲明指向?qū)崿F(xiàn)該抽象類的具體類的指針或引用;抽象類派生的類的純虛函數(shù)沒有被改寫,那么,它的派生類還是個(gè)抽象類。定義純虛函數(shù)就是為了讓基類不可實(shí)例化,因?yàn)閷?shí)例化這樣的抽象數(shù)據(jù)結(jié)構(gòu)本身并沒有意義,或者給出實(shí)現(xiàn)也沒有意義。
2、虛函數(shù)聲明如下:virtual ReturnType FunctionName(Parameter);虛函數(shù)必須實(shí)現(xiàn),如果不實(shí)現(xiàn),編譯器將報(bào)錯(cuò)。
3、對(duì)于虛函數(shù)來說,父類和子類都有各自的版本。由多態(tài)方式調(diào)用的時(shí)候動(dòng)態(tài)綁定。
4、實(shí)現(xiàn)了純虛函數(shù)的子類,該純虛函數(shù)在子類中就編程了虛函數(shù),子類的子類即孫子類可以覆蓋該虛函數(shù),由多態(tài)方式調(diào)用的時(shí)候動(dòng)態(tài)綁定。
5、虛函數(shù)是C++中用于實(shí)現(xiàn)多態(tài)(polymorphism)的機(jī)制。核心理念就是通過基類訪問派生類定義的函數(shù)。
6、在有動(dòng)態(tài)分配堆上內(nèi)存的時(shí)候,析構(gòu)函數(shù)必須是虛函數(shù),但沒有必要是純虛的。
7、友元不是成員函數(shù),只有成員函數(shù)才可以是虛擬的,因此友元不能是虛擬函數(shù)。但可以通過讓友元函數(shù)調(diào)用虛擬成員函數(shù)來解決友元的虛擬問題。
8、析構(gòu)函數(shù)應(yīng)當(dāng)是虛函數(shù),將調(diào)用相應(yīng)對(duì)象類型的析構(gòu)函數(shù),因此,如果指針指向的是子類對(duì)象,將調(diào)用子類的析構(gòu)函數(shù),然后自動(dòng)調(diào)用基類的析構(gòu)函數(shù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容