16

Recently I have attended one C++ technical interview: during that interviewer asked me one question which I was not able to answer: Even I tried on internet and some forums however unable to get the answer, Please see the code snippet below:

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;
}

Description: There are two base classes named Base1 and Base2 and one derived class named Derived. Derived is multiple inherited from Base1 and Base2.

The question:I want Derived should be inherited only from one class not from both. If developer will try to inherit from both classes: then the error should generate: let me summarize it:

  • Scenerio 1: class Derived : public Base1 // Ok.---> No Error
  • Scenerio 2: class Derived : public Base2 // Ok.---> No Error
  • Scenerio 3: class Derived : public Base1, public Base2 // Error or exception or anything. Not able to inherit.

Note: Can you answer this problem: I am really not sure whether this is feasible or not. And one more thing: this is not a diamond problem.

Thanks.

Netherwire
  • 2,669
  • 3
  • 31
  • 54
hims
  • 325
  • 3
  • 10
  • 1
    You could argue that C++ is probably the wrong language in which to take such an approach; it's best to either allow multiple inheritance and use C++, or switch to a language that disallows multiple inheritance. I'm curious to see if there's a solution, though. – Zenexer Dec 19 '13 at 08:28

5 Answers5

29

Declare a pure virtual function in both bases that have a different return type:

class B1 {
   virtual void a() = 0;
};

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

It's impossible to inherit from both.

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;
}

This error is produced:

  • error C2555: 'D::a': overriding virtual function return type differs and is not covariant from 'B1::a'
  • see declaration of 'B1::a'
egur
  • 7,830
  • 2
  • 27
  • 47
  • 1
    Finally the awesome answer. – Netherwire Dec 19 '13 at 09:53
  • I tried with above code, however couldn't get any error/warning. etc. – hims Dec 19 '13 at 10:34
  • I'll update with derived class and `main`. done – egur Dec 19 '13 at 12:21
  • 1
    It's true, now you cannot inherit from both base classes at the same time. On the other hand, it is kind of a burden to ask the client (`D`) to override functions just for the sake of it. – blackbird Dec 19 '13 at 12:27
  • The ugliness can be masked with macros. – egur Dec 19 '13 at 14:47
  • 3
    This answers the immediate scenario regarding *direct* inheritance, but still fails with *indirect* inheritance. I.e `class D1: public B1` , `class D2 : public B2`, and finally `class D : public D1, public D2` will compile successfully when D1 and D2 implement their respective base pure virtuals. – WhozCraig Dec 19 '13 at 15:34
4

You can do this with C++11, look:

#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; }

It works and compiles only if you inherit from Base1 or from Base2. If you try to inherit from both, it doesn't compile. If you want a run-time error, instead of a compile time error, you could even use the same method to check that. Try to play with this snippet.

Marco Pagliaricci
  • 1,366
  • 17
  • 31
2

A run time check:

#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;
}

Note: This does not work if classes are empty. Not as good as the excellent idea of @egur. It has the benefit of not introducing virtual functions, though.

0

Use the method suggested by Lai Yu-Hsuan but give Base0 a pure virtual function that has to be overridden by Base1 and Base2. The compiler should complain about ambiguous function if you try to inherit from both Base1 and Base2. Edit: This is not the correct answer as pointed by Mike below

o_weisman
  • 804
  • 7
  • 19
  • 2
    Only if this function is called somewhere inside the code. If it is not, the compiler doesn't bother updating the virtual table. this leaves us with attempting to force the user to call this function at least once, so "almot" but "no cigar". – NiRR Dec 19 '13 at 09:29
  • @NiRR In that case we can call it from the constructor of each base class. Can I have my cigar now? – o_weisman Dec 19 '13 at 09:38
  • @o_weisman: That wouldn't cause an error. You'll only get an error if it's called in the context of the derived class. – Mike Seymour Dec 19 '13 at 09:39
  • @o_weisman nope. you can go ahead and try it. during the construction of Bas01 or Base2, there is no ambiguity yet. Only after the contsruction of Derived - but you can't force the implementation of Derived. – NiRR Dec 19 '13 at 09:42
  • @MikeSeymour wouldn't it be called when the bases are instantiated? Or are you saying this is not enough? – o_weisman Dec 19 '13 at 09:42
  • @o_weisman: Calling a virtual function from a base class's constructor or destructor selects the override in that base class, not the final override. So no - it will (unambiguously) call the base class version, with undefined behaviour if it's pure virtual in the base class. – Mike Seymour Dec 19 '13 at 09:44
  • @MikeSeymour In that case you could use my other suggestion of having a pure virtual with the same name and signature in each of the base classes, but that is just ugly and does not show the real intent. – o_weisman Dec 19 '13 at 09:46
  • @o_weisman: Indeed, as long as they have conflicting return types so that you can't override both of them - as described, your suggestion won't work either. – Mike Seymour Dec 19 '13 at 09:53
  • I stand corrected, been programming in C# for too long – o_weisman Dec 19 '13 at 09:58
-1

The only way I can figure out is to force this situation to be a diamond problem.

class Base1 : public Base0
class Base2 : public Base0

Then if the user tries to inherit from both Base1 and Base2 the compiler will complain.

Lai Yu-Hsuan
  • 27,509
  • 28
  • 97
  • 164
  • 4
    with which compiler have you tried that? because as far as I know (and have verified with vs2010) compilers won't complain for the code you show, and why should they? it's the classical "problematical" example given for multiple inheritance, but that is not because it's not allowed, but because it can cause problems... – codeling Dec 19 '13 at 08:30
  • No complain with "g++ -Wall" – Johan Dec 19 '13 at 08:32
  • 6
    Why would the compiler complain? It's perfectly valid to derive from two classes with a common base, and this isn't a diamond since the inheritance isn't virtual. – Mike Seymour Dec 19 '13 at 08:32