If we have diamond inheritance and use public virtual base classes, we can stop the first constructor from being called multiple times. Now, I'd like to do the same sort of thing for functions outside of the constructor. For example, the code:
#include <iostream>
struct A {
virtual void foo() {
std::cout << "A" << std::endl;
}
};
struct B : virtual public A {
virtual void foo() {
A::foo();
std::cout << "B" << std::endl;
}
};
struct C : virtual public A {
virtual void foo() {
A::foo();
std::cout << "C" << std::endl;
}
};
struct D : public B, public C{
virtual void foo() {
B::foo();
C::foo();
std::cout << "D" << std::endl;
}
};
int main() {
D d;
d.foo();
}
produces the result
A
B
A
C
D
I'd like to modify it so that it just produces
A
B
C
D
What sort of strategies or patterns accomplish this?
EDIT 1
I like Tony D's answer better than the following. Nonetheless, it's in theory possible to use constructors of another class in order to define the proper hierarchy of functions. Specifically
#include <iostream>
struct A;
struct B;
struct C;
struct D;
namespace foo {
struct A {
A(::A* self);
};
struct B : virtual public A {
B(::B* self);
};
struct C : virtual public A {
C(::C* self);
};
struct D : public B, public C{
D(::D* self);
};
}
struct A {
private:
friend class foo::A;
friend class foo::B;
friend class foo::C;
friend class foo::D;
int data;
public:
A() : data(0) {}
virtual void foo() {
(foo::A(this));
}
void printme() {
std::cout << data << std::endl;
}
};
struct B : virtual public A {
virtual void foo() {
(foo::B(this));
}
};
struct C : virtual public A {
virtual void foo() {
(foo::C(this));
}
};
struct D : public B, public C{
virtual void foo() {
(foo::D(this));
}
};
foo::A::A(::A* self) {
self->data+=1;
std::cout << "A" << std::endl;
}
foo::B::B(::B* self) : A(self) {
self->data+=2;
std::cout << "B" << std::endl;
}
foo::C::C(::C* self) : A(self) {
self->data+=4;
std::cout << "C" << std::endl;
}
foo::D::D(::D* self) : A(self), B(self), C(self) {
self->data+=8;
std::cout << "D" << std::endl;
}
int main() {
D d;
d.foo();
d.printme();
}
Basically, the classes inside of the namespace foo do the computation for the function named foo. This seems a little verbose, so perhaps there's a better way to do it.
EDIT 2
Thanks again to Tony D for clarifying the above example. Yes, essentially what the above does is create temporary variables that adhere to the virtual base designation. In this way, we can use the constructor in order to prevent redundant computations. The extra cruft was to try and show how to get access to access to private members that may have been buried in the base class. Thinking about it a little bit more, there's another way to do this, which may or may not be cleaner depending on the application. I'll leave it here for reference. As with the last example, the weakness is that we're essentially required to wire the the inheritance again, by hand.
#include <iostream>
struct A {
protected:
int data;
public:
A() : data(0) {}
struct foo{
foo(A & self) {
self.data+=1;
std::cout << "A" << std::endl;
}
};
void printme() {
std::cout << data << std::endl;
}
};
struct B : virtual public A {
struct foo : virtual public A::foo {
foo(B & self) : A::foo(self) {
self.data+=2;
std::cout << "B" << std::endl;
}
};
};
struct C : virtual public A {
struct foo : virtual public A::foo {
foo(C & self) : A::foo(self) {
self.data+=4;
std::cout << "C" << std::endl;
}
};
};
struct D : public B, public C{
struct foo : public B::foo, public C::foo {
foo(D & self) : A::foo(self) , B::foo(self), C::foo(self) {
self.data+=8;
std::cout << "D" << std::endl;
}
};
};
int main() {
D d;
(D::foo(d));
d.printme();
}
Essentially, the call (D::foo(d)) creates a temporary who's constructor does the actions we desire. We pass in the object d by hand in order to access to the memory. Since the classes foo are inside of the classes A..D, this gives us access to the protected members.