C++:Inheritance
lastmod 2018-08-17created 2015-04-028 edits
Notes on inheritance in C++.
Virtual inheritance
An example that causes problems with multiple inheritance is as follows.
#include <iostream>
using namespace std;
struct A {
int ii;
A(int i) : ii(i) { cout << "A:" << i << endl; }
void printFromA() { cout << "ii: " << ii << std::endl; }
};
struct B : public A {
B(int i) : A(i) { cout << "B:" << i << endl; }
void printFromB() { cout << "ii: " << ii << std::endl; }
};
struct C : public A {
C(int i) : A(i) { cout << "C:" << i << endl; }
void printFromC() { cout << "ii: " << ii << std::endl; }
};
struct D : public B, public C {
D(int b, int c) : B(b), C(c) { cout << "D:" << b << "," << c << endl; }
// void printFromD() { cout << "ii: " << ii << std::endl; } // ambiguous error.
};
int main()
{
D d(1, 2);
//d.printFromA(); // ambiguous error.
d.printFromB();
d.printFromC();
return 0;
}
As shown below, the A constructor is called twice. (B and C each hold their own separate A.)
To solve this, apply virtual inheritance. (The unneeded print~ functions are removed.)
#include <iostream>
using namespace std;
struct A {
A(int i) { cout << "A:" << i << endl; }
};
struct B : virtual public A {
B(int i) : A(i) { cout << "B:" << i << endl; }
};
struct C : virtual public A {
C(int i) : A(i) { cout << "C:" << i << endl; }
};
struct D : public B, public C {
D(int b, int c) : B(b), C(c) { cout << "D:" << b << "," << c << endl; }
};
int main()
{
D d(1, 2);
return 0;
}
Doing it this way produces the following error.
test.cpp: In constructor ‘D::D(int, int)’:
test.cpp:30:32: error: no matching function for call to ‘A::A()’
D(int b, int c) : B(b), C(c)
^
test.cpp:6:5: note: candidate: A::A(int)
A(int i)
^
test.cpp:6:5: note: candidate expects 1 argument, 0 provided
test.cpp:4:8: note: candidate: A::A(const A&)
struct A
^
test.cpp:4:8: note: candidate expects 1 argument, 0 provided
The virtual keyword is a keyword for late binding. So the inheritance does not happen in B and C, but through D. Therefore it must finally be changed as follows.
#include <iostream>
using namespace std;
struct A {
A(int i) { cout << "A:" << i << endl; }
};
struct B : virtual public A {
B(int i) : A(i) { cout << "B:" << i << endl; }
};
struct C : virtual public A {
C(int i) : A(i) { cout << "C:" << i << endl; }
};
struct D : public B, public C {
D(int b, int c) : A(100), B(b), C(c) { cout << "D:" << b << "," << c << endl; }
};
int main()
{
D d(1, 2);
return 0;
}
The initialization code A(100) was added to the D constructor. The output is as follows.
Non inheritance policy
class SomeClass : private virtual boost::noncopyable
{
public:
//< virtual 키워드는 구현 코드의 지연을 의미한다. 따라서 실제 객체를 선언할때에 판별 될 것이다.
};
For reference, in C++11 this is easily solved with final.
Troubleshooting
virtual-move-assign
GCC may produce the following error.
defaulted move assignment for 'B' calls a non-trivial move assignment operator for virtual base 'A' [-Wvirtual-move-assign]
Here is an example.
struct Base
{
Base() = default;
Base& operator=(Base&&)
{
std::cout << __PRETTY_FUNCTION__ << '\n';
return *this;
}
};
struct A : virtual Base {};
struct B : virtual Base {};
struct C : A, B {};
int main()
{
C c;
c = C();
}
The output is as follows:
This shows that the single Base instance was moved twice, once by each of the move assignment operators of A and B. The second move assignment takes from an already moved-from object and can overwrite the contents of the first move assignment. Clang emits a warning related to -Wmultiple-move-vbase. The way to fix this is to implement move assignment operators for A and B so that they detect that Base has already been moved and skip the second move assignment.
Favorite site
- How to prevent class inheritance in C++
- Prevent class inheritance in C++
- Making a class that cannot be inherited