c++ - 多重继承 - c ++钻石问题 - 如何只调用一次基本方法

我在c中使用了多个继承,并通过显式调用它们的基类来扩展基本方法。 假设以下层次结构:


 Creature


/


 Swimmer Flier 


 /


 Duck



对应于


class Creature


{


 public:


 virtual void print() 


 {


 std::cout <<"I'm a creature" <<std::endl;


 }


};



class Swimmer : public virtual Creature


{


 public:


 void print() 


 {


 Creature::print();


 std::cout <<"I can swim" <<std::endl;


 }


};



class Flier : public virtual Creature


{


 public:


 void print() 


 {


 Creature::print();


 std::cout <<"I can fly" <<std::endl;


 }


};



class Duck : public Flier, public Swimmer 


{


 public:


 void print() 


 {


 Flier::print();


 Swimmer::print();


 std::cout <<"I'm a duck" <<std::endl;


 }


};



现在这提出了一个问题- 调用duck方法的 print 调用它的' 各自的基本方法,所有这些方法又调用 Creature::print() 方法,因此最终被调用两次-


I'm a creature


I can fly


I'm a creature


I can swim 


I'm a duck



我想找到一个方法来确保只调用一次基方法。 类似于虚拟继承的工作方式(在第一次调用时调用基础构造函数,然后在其他派生类的连续调用中仅指定指向它的指针)。

是否有一些内置的方法可以做到这一点,还是我们需要自己实现一个 ?

如果是这样,你会如何处理?

更新:问题不是特定打印的。 我想知道是否有一种扩展基本方法和功能的机制,同时保持调用顺序, 并避免钻石问题。

我现在明白了,最突出的解决方案是添加 helper 方法。 我只是想知道这是否是"干净"的方法。

时间:

很有可能是XY问题。 但是......别调用它两次。


#include <iostream>



class Creature


{


public:


 virtual void identify()


 {


 std::cout <<"I'm a creature" <<std::endl;


 }


};



class Swimmer : public virtual Creature


{


public:


 virtual void identify() override


 {


 Creature::identify();


 tell_ability();


 std::cout <<"I'm a swimmern";


 }



 virtual void tell_ability()


 {


 std::cout <<"I can swimn";


 }


};



class Flier : public virtual Creature


{


public:


 virtual void identify() override


 {


 Creature::identify();


 tell_ability();


 std::cout <<"I'm a fliern";


 }



 virtual void tell_ability()


 {


 std::cout <<"I can flyn";


 }


};



class Duck : public Flier, public Swimmer


{


public:


 virtual void tell_ability() override


 {


 Flier::tell_ability();


 Swimmer::tell_ability();


 }



 virtual void identify() override


 {


 Creature::identify();


 tell_ability();


 std::cout <<"I'm a duckn";


 }


};



int main()


{


 Creature c;


 c.identify();


 std::cout <<"------------------n";



 Swimmer s;


 s.identify();


 std::cout <<"------------------n";



 Flier f;


 f.identify();


 std::cout <<"------------------n";



 Duck d;


 d.identify();


 std::cout <<"------------------n";


}



输出:


I'm a creature


------------------


I'm a creature


I can swim


I'm a swimmer


------------------


I'm a creature


I can fly


I'm a flier


------------------


I'm a creature


I can fly


I can swim


I'm a duck


------------------



我们可以让基类跟踪这些属性:


#include <iostream>


#include <string>


#include <vector>



using namespace std::string_literals;



class Creature


{


public:


 std::string const attribute{"I'm a creature"s};


 std::vector<std::string> attributes{attribute};


 virtual void print()


 {


 for (auto& i : attributes)


 std::cout <<i <<std::endl;


 }


};



class Swimmer : public virtual Creature


{


public:


 Swimmer() { attributes.push_back(attribute); }


 std::string const attribute{"I can swim"s};


};



class Flier : public virtual Creature


{


public:


 Flier() { attributes.push_back(attribute); }


 std::string const attribute{"I can fly"s};


};



class Duck : public Flier, public Swimmer


{


public:


 Duck() { attributes.push_back(attribute); }


 std::string const attribute{"I'm a duck"s};


};



int main()


{


 Duck d;


 d.print();


}



同样,如果它不仅仅是打印,而是函数调用,那么我们可以让基类跟踪函数:


#include <iostream>


#include <functional>


#include <vector>



class Creature


{


public:


 std::vector<std::function<void()>> print_functions{[this] {Creature::print_this(); }};


 virtual void print_this()


 {


 std::cout <<"I'm a creature" <<std::endl;


 }


 void print()


 {


 for (auto& f : print_functions)


 f();


 }


};



class Swimmer : public virtual Creature


{


public:


 Swimmer() { print_functions.push_back([this] {Swimmer::print_this(); }); }


 void print_this()


 {


 std::cout <<"I can swim" <<std::endl;


 }


};



class Flier : public virtual Creature


{


public:


 Flier() { print_functions.push_back([this] {Flier::print_this(); }); }


 void print_this()


 {


 std::cout <<"I can fly" <<std::endl;


 }


};



class Duck : public Flier, public Swimmer


{


public:


 Duck() { print_functions.push_back([this] {Duck::print_this(); }); }


 void print_this()


 {


 std::cout <<"I'm a duck" <<std::endl;


 }


};



int main()


{


 Duck d;


 d.print();


}



你对 print 方法的显式调用构成了问题的关键。

一种方法是删除 print 调用,并将它的替换为


void queue(std::set<std::string>& data)



然后把打印的信息放入 set 。 那么继承中的函数不止一次被调用并不重要。

然后,在 Creature 中的单个方法中实现该集合的打印。

如果你想保留打印顺序,那么你需要将 set 替换为另一个尊重插入顺序的容器并拒绝重复 。

如果你的中间类方法不调用基类方法,最简单和最简单的方法是提取额外的方法,然后重新实现Print很容易。


class Creature


{


 public:


 virtual void print() 


 {


 std::cout <<"I'm a creature" <<std::endl;


 }


};



class Swimmer : public virtual Creature


{


 public:


 void print() 


 {


 Creature::print();


 detailPrint();


 }



 void detailPrint()


 {


 std::cout <<"I can swim" <<std::endl;


 }


};



class Flier : public virtual Creature


{


 public:


 void print() 


 {


 Creature::print();


 detailPrint();


 }



 void detailPrint()


 {


 std::cout <<"I can fly" <<std::endl;


 }


};



class Duck : public Flier, public Swimmer 


{


 public:


 void print() 


 {


 Creature::Print();


 Flier::detailPrint();


 Swimmer::detailPrint();


 detailPrint();


 }



 void detailPrint()


 {


 std::cout <<"I'm a duck" <<std::endl;


 }


};



没有详细描述问题,你的实际问题是很难找到更好的解决方案。

简单的方法是创建一系列 helper 类,它们模仿主层次结构的继承结构并在它的构造函数中执行所有打印。


 struct CreaturePrinter {


 CreaturePrinter() { 


 std::cout <<"I'm a creaturen";


 }


 };



 struct FlierPrinter: virtual CreaturePrinter.. . 


 struct SwimmerPrinter: virtual CreaturePrinter.. .


 struct DuckPrinter: FlierPrinter, SwimmerPrinter.. .



然后主层次结构中的每个打印方法只创建相应的helper 类。 没有手动链接。

为了便于维护,你可以使每个打印类嵌套在它的对应的主类中。

在大多数真实世界中,你希望将对主对象的引用作为参数传递给它的helper的构造函数。

...