用C ++反映类的继承树?

时间:2011-02-17 09:14:56

标签: c++ inheritance

假设我在C ++中有以下类,我想检查它们的继承:

Vehicle

MotorcarVehicle
AircraftVehicle

Biplane AircraftVehicle 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()

有没有一种优雅的方式来写这个?

(谢谢你的想法!)

6 个答案:

答案 0 :(得分: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;
}

输出(装饰名称):

  

10Helicopter - 8Aircraft - 7Vehicle

见ideone:http://www.ideone.com/5PoJ0

如果需要,您可以剥去装饰。但它将是特定于编译器的! Here是一个使用remove_decoration函数去除装饰的版本,然后输出变为:

  

直升机 - 飞机 - 车辆

顺便说一下,正如我所说,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 :(得分: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" )

答案 2 :(得分:2)

  

[...]但是试图避免复制和粘贴getClassLineage()。

据我所知,这是不可能的。 C ++本身没有反射,所以程序员必须自己完成工作。以下C ++ 0x版本在Visual Studio 2010下可以使用,但我不能说其他编译器:

#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的编译器。

答案 3 :(得分: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;
}

答案 4 :(得分: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

答案 5 :(得分: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;
}