《C++編程風格》第四章:虛函數

原書抄錄:

  • 組件之間的交互關係更少並且更簡單,將使得程序更容易理解和維護。(低耦合,高內聚

  • 當我們要決定在一個類中到底是使用數據成員還是函數成員來表示一個特性時,我們首先應該考慮:
    這個特性是用屬性值來描述的還是由用行爲來描述的?

    • 如果特性是一個屬性值,那麼用數據成員來表示就更簡單。派生類對象將使用這個數據成員,並且可使用它的值。
    • 如果這個特性是一種行爲(一種操作或者算法),那麼我們應該用成員函數來表示它。這個成員函數可以是虛函數,也可以是非虛函數。
  • 我們還要進一步提出問題:
    派生類和基類是否有着同樣的行爲?

    • 派生類必須表現出不同的行爲,那麼就應該使用虛函數,這樣每個派生類都給出自己的實現。
    • 如果所有的派生類都表現出相同的行爲,那麼他們就可以共享一個在基類中定義的非虛函數
  • 使用虛函數的開銷是難以確定的 。包含虛函數的類中存在虛指針,指向一個描述虛函數的表。


規則總結:

  • 派生類在處理繼承而來的狀態時必須與基類保持一致。(一致性)
  • 如果在公有基類中沒有定義虛析構函數,那麼在所有的派生類或者派生類的數據成員中都應該沒有定義虛析構函數。
  • 通常情況下,公有基類的析構函數應該被聲明爲虛函數。
  • 將公有的行爲遷移到基類中。
  • 降低耦合性 —— 將類之間的交互最小化。
  • 如果派生類之間的區別在於屬性,則用數據成員來表示,如果在於行爲,則用虛函數來表示。
  • 沒有哪個類是完美的;過窄的設計要好於過寬的設計。
  • 我們應該考慮使用默認參數的形式來代替函數重載的形式。

章後習題:

很簡單,題目就不寫了。
答案:是因爲group是直接賦值得到的,沒有申請內存,自然也不需要刪除了。


最後附上本章中研究的代碼前後對比。
最初的代碼(略有修改):

#include <iostream>
#include <cstring>

class Vehicle{
protected:
    char *plate;
public:
    Vehicle(){  plate = NULL; }

    Vehicle(char *p)
    {
        plate = new char[strlen(p) + 1];
        strcpy(plate, p);
    }

    ~Vehicle(){ delete [] plate; }

    virtual void identify()
    {
        std::cout << "generic vehicle" << std::endl;
    }
};

class Car: public Vehicle{
public:
    Car(): Vehicle() {}
    Car(char *p): Vehicle(p) {}
    void identify()
    {
        std::cout << "car with plate " << plate << std::endl;
    }
};

class Truck: public Vehicle{
public:
    Truck(): Vehicle() {}
    Truck(char *p): Vehicle(p) {} 
    void identify()
    {
        const char *p = plate ? plate : "<none>";
        std::cout << "truck with plate " << p << std::endl;
    }
};

class Garage{
    int maxVehicles;
    Vehicle **parked;

public:
    Garage(int max);
    ~Garage();
    int accept(Vehicle *);
    Vehicle *release(int bay);
    void listVehicles();
};

Garage::Garage(int max)
{
    maxVehicles = max;
    parked = new Vehicle* [maxVehicles];
    for(int bay = 0; bay < maxVehicles; ++ bay){
        parked[bay] = NULL;
    }
}

Garage::~Garage()
{
    delete [] parked;
}

int Garage::accept(Vehicle *veh)
{
    for(int bay = 0; bay < maxVehicles; ++ bay){
        if(! parked[bay]){
            parked[bay] = veh;
            return bay;
        }
    }
    return -1;
}

Vehicle* Garage::release(int bay)
{
    if(bay < 0 || bay >= maxVehicles){
        return NULL;
    }
    Vehicle *veh = parked[bay];
    parked[bay] = NULL;
    return veh;
}

void Garage::listVehicles()
{
    for(int bay = 0; bay < maxVehicles; ++ bay){
        if(parked[bay]){
            std::cout << "Vehicle int bay " << bay << " is :";
            parked[bay]->identify();
        }
    }
}

Car c1("RVR 101");
Car c2("SPT 202");
Car c3("CHP 303");
Car c4("BDY 404");
Car c5("BCH 505");

Truck t1("TBL 606");
Truck t2("IKY 707");
Truck t3("FFY 808");
Truck t4("PLS 909");
Truck t5("SLY 000");

int main()
{
    Garage Park(15);
    Park.accept(&c1);

    int t2bay = Park.accept(&t2);
    Park.accept(&c3);
    Park.accept(&t1);

    int c4bay = Park.accept(&c4);
    Park.accept(&c5);
    Park.accept(&t5);

    Park.release(t2bay);
    Park.accept(&t4);
    Park.accept(&t3);

    Park.release(c4bay);
    Park.accept(&c2);
    Park.listVehicles();
    return 0;
}

經過作者(當然是書的作者了)的分析總結修改,最後的代碼(運用這些規則後):

#include <iostream>
#include <cstring>

class Vehicle
{
protected:
    char *plate;
    char *group;

public:
    Vehicle(char *g, char *p);
    virtual ~Vehicle() = 0;
    void identify();
};

Vehicle::Vehicle(char *g, char *p)
{
    group = g;
    if(p){
        plate = new char[strlen(p) + 1];
        strcpy(plate, p);
    }
    else{
        plate = NULL;
    }
}

Vehicle::~Vehicle()
{
    if(plate){
        delete [] plate;
    }
}

void Vehicle::identify()
{
    const char *p = plate ? plate : "<none>";
    std::cout << group << " with plate " << p << std::endl;
}


class Car: public Vehicle{
public:
    Car(char *p = NULL): Vehicle("car", p) {}
};

class Truck: public Vehicle{
public:
    Truck(char *p = NULL): Vehicle("truck", p) {}
};


class Garage{
    int maxVehicles;
    Vehicle **parked;

public:
    Garage(int max);
    ~Garage();
    int accept(Vehicle *);
    Vehicle *release(int bay);
    void listVehicles();
};

Garage::Garage(int max)
{
    maxVehicles = max;                                                                                                                       
    parked = new Vehicle* [maxVehicles];
    for(int bay = 0; bay < maxVehicles; ++ bay){
        parked[bay] = NULL;
    }
}

Garage::~Garage()
{
    delete [] parked;
}

int Garage::accept(Vehicle *veh)
{
    for(int bay = 0; bay < maxVehicles; ++ bay){
        if(! parked[bay]){
            parked[bay] = veh;
            return bay;
        }
    }
    return -1;
}

Vehicle* Garage::release(int bay)
{
    if(bay < 0 || bay >= maxVehicles){
        return NULL;
    }
    Vehicle *veh = parked[bay];
    parked[bay] = NULL;
    return veh;
}

void Garage::listVehicles()
{
    for(int bay = 0; bay < maxVehicles; ++ bay){
        if(parked[bay]){
            std::cout << "Vehicle int bay " << bay << " is :";
            parked[bay]->identify();
        }
    }
}

Car c1("RVR 101");
Car c2("SPT 202");
Car c3("CHP 303");
Car c4("BDY 404");
Car c5("BCH 505");

Truck t1("TBL 606");
Truck t2("IKY 707");
Truck t3("FFY 808");
Truck t4("PLS 909");
Truck t5("SLY 000");

int main()
{
    Garage Park(15);
    Park.accept(&c1);

    int t2bay = Park.accept(&t2);
    Park.accept(&c3);
    Park.accept(&t1);

    int c4bay = Park.accept(&c4);
    Park.accept(&c5);
    Park.accept(&t5);

    Park.release(t2bay);
    Park.accept(&t4);
    Park.accept(&t3);

    Park.release(c4bay);
    Park.accept(&c2);
    Park.listVehicles();
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章