【问题标题】:prevent multiple inheritance in C++ [duplicate]防止C ++中的多重继承[重复]
【发布时间】:2014-01-07 17:20:57
【问题描述】:

最近我参加了一个C++技术面试:面试官问了我一个问题,我没能回答:即使我在互联网和一些论坛上尝试过但无法得到答案,请看下面的代码sn-p :

using namespace std;

class Base1
{
    public:
    Base1() 
    {
        cout << "Base1 constructor..." << endl;
    }

    ~Base1() 
    {
        cout << "Base1 Destructor..." << endl;
    }
};  

class Base2
{
public:
    Base2() 
    {
        cout << "Base2 constructor..." << endl;
    }

    ~Base2() 
    {
      cout << "Base2 Destructor..." << endl;  
    }
};

class Derived : public Base1, public Base2 
{
public:
  Derived()
  {
      cout << "Derived constructor...."  << endl;
  }
  ~Derived()
  {
      cout << "Derived Destructor..." << endl;
   }
};


int main()
{
   cout << "Hello World" << endl; 
   Base1 b1; Base2 b2;
   Derived d1;

   return 0;
}

说明:有两个名为 Base1 和 Base2 的基类和一个名为 Derived 的派生类。 Derived 是从 Base1 和 Base2 多重继承的。

问题:我希望 Derived 只能从一个类继承,而不是从两个类继承。如果开发人员将尝试从这两个类继承:那么应该会产生错误:让我总结一下:

  • 场景 1:派生类:public Base1 // 好的。---> 没有错误
  • 场景 2:派生类:public Base2 // 好的。---> 没有错误
  • 场景 3:派生类:public Base1、public Base2 // 错误或异常或任何事情。无法继承。

注意:你能回答这个问题吗:我真的不确定这是否可行。还有一件事:这不是钻石问题。

谢谢。

【问题讨论】:

  • 您可能会争辩说,C++ 可能是采用这种方法的错误语言;最好是允许多重继承并使用 C++,或者切换到不允许多重继承的语言。不过,我很想知道是否有解决方案。

标签: c++ inheritance multiple-inheritance


【解决方案1】:

运行时检查:

#include <iostream>
#include <stdexcept>
#include <typeinfo>

class B1 {
    protected:
    template <typename D>
    B1(D* d) {
        // The paranoid 'is_same' is preventing the cast (B1*)this in the derived constructor.
        if((void*)this != (void*)d || std::is_same< B1, D >::value)
            throw std::logic_error("Multiple Inheritance [1]");
    }

    int data;
};

class B2 {
    protected:
    template <typename D>
    B2(D* d) {
        // The paranoid 'is_same' is preventing the cast (B2*)this in the derived constructor.
        if((void*)this != (void*)d || std::is_same< B2, D >::value)
            throw std::logic_error("Multiple Inheritance [2]");
    }

    int data;
};


struct D : public B1, public B2 {
    D()
    :   B1(this), B2(this)
    {}
};

int main() {
    D d;
}

注意:如果类为空,这将不起作用。 不如@egur 的好主意。 不过,它的好处是不引入虚函数。

【讨论】:

    【解决方案2】:

    在具有不同返回类型的两个基中声明一个纯虚函数:

    class B1 {
       virtual void a() = 0;
    };
    
    class B2 {
       virtual int a() = 0; // note the different return type
    };
    

    不可能从两者继承。

    class D : public B1, public B2 {
    public:
        // virtual void a() {} // can't implement void a() when int a() is declared and vice versa
        virtual int  a() {}
    };
    
    int main(void) {
        D d; // produces C2555 error
        return 0;
    }
    

    产生此错误:

    • 错误 C2555:“D::a”:覆盖虚函数返回类型不同,并且与“B1::a”不协变
    • 参见“B1::a”的声明

    【讨论】:

    • 终于得到了很棒的答案。
    • 我尝试了上面的代码,但是没有得到任何错误/警告。等等
    • 我将更新派生类和main。完成
    • 是的,现在你不能同时继承两个基类。另一方面,要求客户端 (D) 仅仅为了它而重写函数是一种负担。
    • 这回答了关于 直接 继承的直接场景,但仍然会因 indirect 继承而失败。即class D1: public B1class D2 : public B2,最后是class D : public D1, public D2,当 D1 和 D2 实现各自的基本纯虚时,将成功编译。
    【解决方案3】:

    使用 Lai Yu-Hsuan 建议的方法,但给 Base0 一个纯虚函数,该函数必须被 Base1 和 Base2 覆盖。如果您尝试从 Base1 和 Base2 继承,编译器应该会抱怨函数不明确。 编辑:这不是下面迈克指出的正确答案

    【讨论】:

    • 仅当此函数在代码中的某处被调用时。如果不是,编译器不会费心更新虚拟表。这让我们试图强迫用户至少调用一次这个函数,所以“almot”但“no cigar”。
    • @NiRR 在这种情况下,我们可以从每个基类的构造函数中调用它。我现在可以抽雪茄了吗?
    • @o_weisman:这不会导致错误。只有在派生类的上下文中调用它才会出错。
    • @o_weisman 不。你可以继续尝试。在Bas01或Base2的构建过程中,还没有歧义。只有在 Derived 的构建之后 - 但你不能强制 Derived 的实现。
    • @MikeSeymour 在实例化基础时不会调用它吗?还是说这还不够?
    【解决方案4】:

    你可以用 C++11 做到这一点,看:

    #include <type_traits>
    struct Base1 {}; 
    struct Base2 {}; 
    
    struct Derived 
       : public Base1
      , public Base2
    { 
      Derived() 
      { 
         static_assert( !(std::is_base_of< Base1, Derived >::value && std::is_base_of< Base2, Derived >:: value), "You cannot inherit from both Base1 and Base2" ); 
      } 
    }; 
    
    
    
    int main() { Derived d; return 0; }
    

    仅当您从 Base1 从 Base2 继承时,它才能工作和编译。 如果您尝试从两者继承,则无法编译。 如果您想要运行时错误,而不是编译时错误,您甚至可以使用相同的方法来检查它。试着玩这个 sn-p。

    【讨论】:

    • 这并没有停止创建 Derived2: Base1, Base2
    • 我认为这个问题只针对 Derived,而不是其他类型
    【解决方案5】:

    我能弄清楚的唯一方法是把这种情况变成钻石问题。

    class Base1 : public Base0
    class Base2 : public Base0
    

    如果用户尝试从Base1Base2 继承,编译器会报错。

    【讨论】:

    • 你用哪个编译器试过了?因为据我所知(并已通过 vs2010 验证)编译器不会抱怨您显示的代码,他们为什么要抱怨?这是为多重继承给出的经典“有问题”的例子,但这不是因为它不被允许,而是因为它可能会导致问题......
    • 不要抱怨“g++ -Wall”
    • 编译器为什么会抱怨?从具有共同基础的两个类派生是完全有效的,这不是钻石,因为继承不是虚拟的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-28
    • 2011-08-22
    • 1970-01-01
    • 2015-07-30
    • 2020-06-08
    • 1970-01-01
    相关资源
    最近更新 更多