2011-02-17 72 views
1

說我有以下類在C++中,我要檢查他們的產業在C++中反映類的繼承樹?

Vehicle

Motorcar是一個Vehicle
Aircraft是一個Vehicle

BiplaneAircraftVehicle
HelicopterAircraftVehicle

我想寫一個方法getClassLineage()做到以下幾點:

Biplane b; 
cout << b.getClassLineage() << endl; // prints "Vehicle--Aircraft--Biplane" 

Helicopter h; 
cout << h.getClassLineage() << endl; // prints "Vehicle--Aircraft--Helicopter" 

Motorcar m; 
cout << m.getClassLineage() << endl; // prints "Vehicle--Motorcar" 

這好像應該是由超一流一次寫它這樣做一個簡單的遞歸的方式,不需翻炒基本上派生類中的每一個都有相同的方法。

假設我們願意申報(僞)在每個派生類的Helicopter.className = "Helicopter"typedef Aircraft baseclass,但儘量避免複製和粘貼getClassLineage()

有沒有一種優雅的方式來寫這個?

(謝謝你的想法!)

+1

你嘗試[TYPE_INFO](http://www.cplusplus.com/reference/std/typeinfo/type_info/ )? – DumbCoder 2011-02-17 09:30:14

回答

4

如果你願意,你可以用虛擬的功能和明確範圍的功能做一個遞歸式的方法調用:

struct vehicle { 
    virtual std::string lineage() const { return "vehicle"; } 
}; 
struct aircraft : vehicle { 
    typedef vehicle base; 
    virtual std::string lineage() const { return base::lineage() + "--aircraft"; } 
}; 
struct biplane : aircraft { 
    typedef aircraft base; 
    virtual std::string lineage() const { return base::lineage() + "--biplane"; } 
}; 
struct nieuport17 : biplane { 
    typedef biplane base; 
    virtual std::string lineage() const { return base::lineage() + "--nieuport17"; } 
}; 
int main() { 
    biplane b; 
    aircraft const & a = b; 
    std::cout << a.lineage() << std::endl; 
} 

它是如何工作的?當你調用v.lineage(),因爲它是一個虛擬函數,它的動態調度將進入biplane::lineage(),因爲這是對象的實際類型。在該函數內部,可以調用其父母的lineage()函數。合格的調用不使用動態調度機制,因此調用將實際在父級執行。基本上,這是怎麼回事:

a.lineage() -- dynamic dispatch --> 
---> biplane::lineage() 
    \__ airplane::lineage() 
     \__ vehigcle::lineage() 
      <-- std::string("vehicle") 
     <-- std::string("vehicle") + "--airplane" 
    <-- std::string("vehicle--airplane") + "--biplane" 
<--- std::string("vehicle--airplane--biplane") 
0

你需要一個靜態字段存儲的血統,而且每個班級都會有自己的血統在它自己的靜態字段追加。

如果你正在考慮使用typeid的()或類似的東西,這是比較複雜的,但將避免getClassLineage()方法的重複,請記住,name字段屬性是煩人(這樣做的原因是超越我)不是類的真實名稱,而是一個可以是該名稱或任何類型的錯位名稱(即未定義的表示)的字符串。

如果我們使用Python或任何其他基於原型的編程語言(您可以繼承由代理實現繼承並因此可以遵循「繼承路徑」),您可以輕鬆應用遞歸方法。

#include <iostream> 
#include <string> 

class Vehicle { 
public: 
    static const std::string Lineage; 

    Vehicle() {} 
    virtual ~Vehicle() {} 

    virtual const std::string &getClassLineage() 
    { return Vehicle::Lineage; } 
}; 

class Motorcar : public Vehicle { 
public: 
    static const std::string Lineage; 

    Motorcar() {} 
    virtual ~Motorcar() {} 

    virtual const std::string &getClassLineage() 
    { return Motorcar::Lineage; } 
}; 

class Helicopter : public Vehicle { 
public: 
    static const std::string Lineage; 

    Helicopter() {} 
    virtual ~Helicopter() {} 

    virtual const std::string &getClassLineage() 
    { return Helicopter::Lineage; } 
}; 

class Biplane : public Vehicle { 
public: 
    static const std::string Lineage; 

    Biplane() {} 
    virtual ~Biplane() {} 

    virtual const std::string &getClassLineage() 
    { return Biplane::Lineage; } 
}; 

const std::string Vehicle::Lineage = "Vehicle"; 
const std::string Motorcar::Lineage = "Vehicle::Motorcar"; 
const std::string Helicopter::Lineage = "Vehicle::Helicopter"; 
const std::string Biplane::Lineage = "Vehicle::Biplane"; 


int main() 
{ 
    Biplane b; 
    std::cout << b.getClassLineage() << std::endl; // prints "Vehicle--Aircraft--Biplane" 

    Helicopter h; 
    std::cout << h.getClassLineage() << std::endl; // prints "Vehicle--Aircraft--Helicopter" 

    Motorcar m; 
    std::cout << m.getClassLineage() << std::endl; // prints "Vehicle--Motorcar" 

    return 0; 
} 
+0

如果您剛剛返回直升機或汽車,這非常棒,但返回整個血統有點脆弱 - 如果您的等級發生變化,Biplane現在來自飛機,那該怎麼辦? – tenpn 2011-02-17 09:44:40

+0

@tenp,是的,這是真的,但正如我在答案的頂部解釋的那樣,我沒有其他辦法可以想到。你一定可以從中創造出衍生品,但它們都會遭受同樣的維護問題。 – Baltasarq 2011-02-17 10:04:50

10

解決方案1 ​​

如果沒有問題與修飾名,那麼你可以寫一個免費函數模板:

struct Vehicle {}; 
struct Aircraft : Vehicle { typedef Vehicle super; }; 
struct Helicopter : Aircraft { typedef Aircraft super; }; 

template<typename T> 
string getClassLineage() 
{ 
    static string lineage = string(typeid(T).name()) +" - " + getClassLineage<typename T::super>(); 
    return lineage; 
} 
template<> 
string getClassLineage<Vehicle>() 
{ 
    static string lineage = string(typeid(Vehicle).name()); 
    return lineage; 
} 

int main() { 
     cout << getClassLineage<Helicopter>() << endl; 
     return 0; 
} 

輸出(修飾的名):

10直升機 - 8Aircraft - 7車輛

看到ideone:http://www.ideone.com/5PoJ0

,如果你想,你可以脫掉裝修。但它會是編譯器特定的!Here是一個版本,其利用remove_decoration函數來剝去裝飾,然後輸出變爲:

直升機 - 飛機 - 車輛

順便說,正如我所說,實施功能是一個編譯器特有的;此外,這可以寫得更正確,因爲我不知道GCC考慮的所有情況,而mangling的類名。但是我希望你能得到基本的想法。


解決方案2

如果沒有問題,在每個派生類中重新定義的函數,那麼這裏有一個簡單的解決辦法:

struct Vehicle 
{ 
    string getClassLineage() const { return "Vehicle"; } 
}; 
struct Aircraft : Vehicle 
{ 
    string getClassLineage() const { return Vehicle::getClassLineage()+" - Aircraft"; } 
}; 
struct Helicopter : Aircraft 
{ 
    string getClassLineage() const { return Aircraft::getClassLineage()+" - Helicopter "; } 
}; 

int main() { 
     Helicopter heli; 
     cout << heli.getClassLineage() << endl; 
     return 0; 
} 

輸出:

車 - 飛機 - 直升機

看到ideone輸出:http://www.ideone.com/Z0Tws

+1

在_Solution 1_中,爲每個類添加一個帶有類名的靜態字符串,並使用它來代替`typeid()。name()`。更快更整潔。 – aaz 2011-02-17 11:49:14

+0

@aaz:啊......不錯的改進。 :-)。謝謝。 – Nawaz 2011-02-17 11:54:39

+0

@aaz:順便說一句,在解決方案2中也可以做同樣的改進。但我希望OP自己能做到這一點! – Nawaz 2011-02-17 12:01:46

2

[...],但儘量避免複製和粘貼getClassLineage()。

據我所知,這是不可能的。 C++本身沒有反射,所以程序員必須親自完成這項工作。 Visual Studio 2010中下以下的C++ 0x版本的作品,但我不能爲其他編譯器說:

#include <string> 
#include <typeinfo> 
#include <iostream> 

class Vehicle{ 
public: 
     virtual std::string GetLineage(){ 
       return std::string(typeid(decltype(this)).name()); 
     } 
}; 

class Aircraft : public Vehicle{ 
public: 
     virtual std::string GetLineage(){ 
       std::string lineage = std::string(typeid(decltype(this)).name()); 
       lineage += " is derived from "; 
       lineage += Vehicle::GetLineage(); 
       return lineage; 
     } 
}; 

class Biplane : public Aircraft{ 
public: 
     virtual std::string GetLineage(){ 
       std::string lineage = std::string(typeid(decltype(this)).name()); 
       lineage += " is derived from "; 
       lineage += Aircraft::GetLineage(); 
       return lineage; 
     } 
}; 

class Helicopter : public Aircraft{ 
public: 
     virtual std::string GetLineage(){ 
       std::string lineage = std::string(typeid(decltype(this)).name()); 
       lineage += " is derived from "; 
       lineage += Aircraft::GetLineage(); 
       return lineage; 
     } 
}; 

int main(){  
     Vehicle v; 
     Aircraft a; 
     Biplane b; 
     Helicopter h; 

     std::cout << v.GetLineage() << std::endl; 
     std::cout << a.GetLineage() << std::endl; 
     std::cout << b.GetLineage() << std::endl; 
     std::cout << h.GetLineage() << std::endl; 

     std::cin.get(); 
     return 0; 
} 

輸出:

class Vehicle * 
class Aircraft * is derived from class Vehicle * 
class Biplane * is derived from class Aircraft * 
class Helicopter * is derived from class Aircraft * 

輸出在ideone略有不同,它的下降星號和裝飾名稱在P開頭的指針,但它的作品。有趣的事實:試圖使用typeid(decltype(*this)).name()爲我編譯VS2010的編譯器。

0

如果使用typeid,則不需要硬編碼字符串(類名稱)。解決你的問題可能是:

#include <iostream> 
#include <typeinfo> 
using namespace std; 

class Vehicle 
{ 
public: 
    Vehicle(); 
    string GetClassLineage(){return strName;} 
protected: 
    string strName; 
}; 

Vehicle::Vehicle() : strName(typeid(*this).name()) 
{ 
    // trim "class " 
    strName = strName.substr(strName.find(" ") + 1); 
} 

class Motorcar : public Vehicle 
{ 
public: 
    Motorcar(); 
}; 

Motorcar::Motorcar() 
{ 
    string strMyName(typeid(*this).name()); 
    strMyName = strMyName.substr(strMyName.find(" ") + 1); 

    strName += " -- "; 
    strName += strMyName; 
} 

int main() 
{ 
    Motorcar motorcar; 
    cout << motorcar.GetClassLineage() << endl; 
    return 0; 
} 

輸出:

Vehicle -- Motorcar 
0
#include <iostream> 
#include <ios> 
#include <iomanip> 
#include <fstream> 
#include <cstdio> 
#include <list> 
#include <sstream> 

using namespace std; 

static const char *strVehicle = "Vehicle"; 
static const char *strMotorcar = "Motorcar"; 
static const char *strHelicopter = "Helicopter"; 

class Vehicle 
{ 
private: 
    const char *ClassName; 
protected: 
    int Lineage; 
    list<const char *> MasterList; 
public: 
    Vehicle(const char *name = strVehicle) 
    { 
     MasterList.push_back(name); 
    } 
    virtual ~Vehicle() {} 
    virtual int getClassLineage() const 
    { 
    return Lineage; 
    } 
    string getName() const 
    { 
     list<const char *>::const_iterator it = MasterList.begin(); 
     ostringstream ss(ios_base::in | ios_base::out); 
     while(it != MasterList.end()) 
     { 
      ss << *(it++); 
      if(it != MasterList.end()) 
       ss << " --> "; 
     } 
     ss << endl; 
     ss << ends; 
     return ss.str(); 
    } 
}; 

class Motorcar : public Vehicle 
{ 
private: 
    const char *ClassName; 
public: 
    Motorcar(const char *name = strMotorcar) 
    { 
     MasterList.push_back(name); 
    } 
    virtual ~Motorcar() {} 
    using Vehicle::getClassLineage; 
    using Vehicle::getName; 
}; 

class Helicopter : public Vehicle 
{ 
private: 
    const char *ClassName; 
public: 
    Helicopter(const char *name = strHelicopter) 
    { 
     MasterList.push_back(name); 
    } 
    virtual ~Helicopter() {} 
    using Vehicle::getClassLineage; 
    using Vehicle::getName; 
}; 


int _tmain(int argc, _TCHAR* argv[]) 
{ 
    Helicopter h; 
    Motorcar m; 
    wcout << "Heli: " << h.getName().c_str() << endl; 
    wcout << "Motorcar: " << m.getName().c_str() << endl; 
    return 0; 
}