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

ell*_*t42 1 c++ inheritance

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

Vehicle

Motorcar是一个Vehicle
AircraftVehicle

BiplaneAircraftVehicle
HelicopterAircraft是一个Vehicle.

我想写一个方法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"
Run Code Online (Sandbox Code Playgroud)

似乎应该有一种简单的递归方式,通过在超类中编写一次,而不必在每个派生类中复制基本相同的方法.

假设我们愿意申报(伪)Helicopter.className = "Helicopter",并 typedef Aircraft baseclass在每一个派生类的,但尽量避免复制和粘贴getClassLineage().

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

(谢谢你的想法!)

Naw*_*waz 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;
}
Run Code Online (Sandbox Code Playgroud)

输出(装饰名称):

10Helicopter - 8Aircraft - 7Vehicle

请参阅ideone:http://www.ideone.com/5PoJ0

如果你愿意,你可以剥掉装饰.但它将是特定于编译器的!是一个使用remove_decoration函数去除装饰的版本,然后输出变为:

直升机 - 飞机 - 车辆

顺便说一下,正如我所说,remove_decoration函数的实现是一个特定的编译器; 另外,这可以用更正确的方式编写,因为我不知道GCC考虑的所有情况,同时修改了类名.但我希望,你得到了基本的想法.


解决方案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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

车辆 - 飞机 - 直升机

请参阅ideone上的输出:http://www.ideone.com/Z0Tws


Dav*_*eas 5

如果你想要一个类似递归的方法,你可以使用虚函数和显式作用域函数调用来实现:

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;
}
Run Code Online (Sandbox Code Playgroud)

它是如何工作的?当你调用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" )
Run Code Online (Sandbox Code Playgroud)

  • 我发现这种递归虚函数方法非常优雅。:) (2认同)