Suppose we have the usual diamond-pattern:
class A
{
public:
virtual char foo() = 0;
virtual ~A() {}
};
class B : public A
{
public:
virtual char foo() { return 'B';}
virtual ~B() {}
};
class C : public A
{
public:
virtual char foo() { return 'C';}
virtual ~C() {}
};
class D : public B, public C
{
public:
virtual char foo() { return 'D';}
virtual ~D() {}
};
Please note that the base class A
does not have any data members. It's in fact just an interface with pure virtual methods.
Now if i do:
D* d = new D();
A* a = static_cast<A*>(d);
the compiler will tell me that the cast is ambiguous as there are two A base classes.
But what if i do:
D* d = new D();
B* b = static_cast<B*>(d); // Or C* c = static_cast<C*>(d);
A* a = static_cast<A*>(b);
Now i have a pointer to one of my A
base classes and i can do a->foo()
.
Is this safe?
What i want to know is if i can do this double-upcasting to just have pointer to interfaces (with no data members) without the overhead of virtual inheritance. I don't plan to downcast the pointer anyway or do anything with it that isn't calling virtual methods.
I know this means having two base classes but as there are no members it shouldn't matter, or does it?
EDIT
:
I'm struggling to find a way to implement interfaces and i think i'll just have to use virtual inheritance.
Suppose i have a class Buffer
(interface class), and a BufferImplementation
class that derives from it.
Now suppose i have another interface IOBuffer
(which derives from the other interface Buffer
), with a IOBufferImplementation
class that must derive both from BufferImplementation
and the IOBuffer
interface.
In my previous example, Buffer is A, BufferImplementation is B, IOBuffer is C, and IOBufferImplementation is D.
Is this safe?
I think so. You are using one of the two paths to reach from the derived class D
to Base A
. You can now use the Interface Members safely.
Just one note, if there are pure virtual methods in A
that is not overridden
in D
, then using D->B->A
path will always call the overridden
method in B
(similar for D->C->A
path), though the implementation in C
was intended/useful. This will also work when A
has data members, and in this particular setup, the data members inherited via D->B->A
will be used.