Pergunta

I have a base class 'Base', which is a pure virtual class:

class Base {

public:
    virtual void A() = 0;
    virtual void B() = 0;

    virtual ~Base() { } // Eclipse complains that a class with virtual members must have virtual destructor
};

I also have 2 other classes, one of them implements A() and the other one implements B():

class DerivedA : public virtual Base
{
public:
    virtual void A() {
        printf("Hello from A");
    }
};

class DerivedB : public virtual Base
{
public:
    virtual void B() {
        printf("Hello from B");
    }
};

The virtual keyword in the declaration should solve the diamond problem.

Now I would like to combine the two classes into another class, so that both A() and B() are implemented, like this:

class DerivedC: public DerivedA, public DerivedB {
     // Now DerivedA and DerivedB are combined
};

// Somewhere else in the code
DerivedC c;
c.A();
c.B();

The problem: Even though G++ compiles the code just fine, Eclipse gives an error: The type 'DerivedC' must implement the inherited pure virtual method 'Base::B'. When compiling with visual studio, I get 2 warnings:

warning C4250: 'DerivedC' : inherits 'DerivedB::DerivedB::B' via dominance
warning C4250: 'DerivedC' : inherits 'DerivedA::DerivedA::A' via dominance

So the question is: what is the correct way of doing this? Does the code above produce undefined behavior?

Note: The title may be a little misleading, I have no idea what a good title for this question would be.

Foi útil?

Solução

What is the correct way of doing this? Does the code above produce undefined behavior?

The code is perfectly valid. There is no Undefined Behavior here.
An unqualified call of A() through a DerivedC class object, will always call DerivedA::A(), while an unqualified call of B() through a DerivedC class object, will always call the DerivedB::B() instance.

Visual C++ gives you a warning because your code uses a less commonly known feature of virtual Inheritance which may not be obvious to most common users and might surprise them. In this case, the warning should be taken as an Informative Nitpick rather than a warning.

Note that the C++ Standard does not restrict compilers from emitting informative warnings for perfectly valid code. The documentation for warning C4250 gives an example which tells you why visual C++ chooses to give this warning.

Outras dicas

You might want to try this :

class DerivedC: public DerivedA, public DerivedB {
public:
    using DerivedA::A;
    using DerivedB::B;
};

I can't test with Eclipse or VC++ myself...

I don't know why a compiler would complain about any of this; this is just the standard mixin technique. Classes Base, DerivedA and DerivedB are abstract, and can't be instantiated, but that's usually the case for mixins. The whole point of a mixin is that it doesn't implement all of the interface. And DerivedC implements both A() and B() through its inherited members.

If a compiler refuses to compile this code, it is broken.

As for warnings... a compiler is free to warn about anything it pleases:

  • There is no requirement that a class with virtual members have a virtual destructor. In practice, it's usually a good idea, however (unless the destructor is protected), and a compiler warning is appropriate.

  • The warnings from Visual Studio are "informative", I guess, but this is the way the language is designed to work. And it's certainly not something to avoid. For that matter, I don't think that dominance actually plays a role here, since the functions in Base are pure virtual. What Visual Studios seems to be trying to say is that in DerivedC, the actual overload of A() is DerivedA::A(), and not Base::A(). Which seems to be what one would intuitively expect to me; the rules concerning dominance are really just a formal statement of what one would intuitively expect.

Anyway, I'd definitely turn the warning about dominance off. There's certainly nothing to worry about in that respect. And I'd complain loudly about a compiler which didn't compile the code.

Visual Studio is known to have a compiler bug issuing warning C4250 in situations where the dominated function is pure virtual. The bug has been closed as "Won't fix"; the accepted solution is to suppress the warning using:

#pragma warning( disable: 4250 ) /* 'class1' : inherits 'class2::member' via dominance */

See also discussion at http://msdn.microsoft.com/en-us/library/6b3sy7ae(VS.80).aspx#CommunityContentHeader.

Your Base class is abstract : it can't be instantiated. The B and A class are also abstract because they implement only one method.

The two solutions are, in file DerivedC.cpp

void DerivedC::A(){ 
   DerivedA::A();
}
void DerivedC::B(){
   Derived:B();
}

Or you can use using keyword in your header-file:

class DerivedC: public DerivedA, public DerivedB {
public:
    using DerivedA::A;
    using DerivedB::B;
};
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top