Diamond Inheritance Lowest Base Class Constructor
The most derived class initializes any virtual base classes. In your class hierarchy, Unknown
must construct the virtual Animal
base class (e.g. by adding Animal(a)
to its initialization list).
When constructing an Unknown
object, neither Fish
nor Bird
will call the Animal
constructor. Unknown
will call the constructor for the Animal
virtual base.
C++ base class function call from last derived in diamond design
Your question seems to not be related to the diamond pattern, but is generally about the inheritance model in C++. Most things in C++ are statically bound, so at compile time the compiler fixes what method or member of a certain name is used:
If you access a member of the implicit this
object or using a pointer or reference to an object, you will end up accessing a member of the class the pointer or reference has at compile time, no matter whether at runtime there is a derived-class object that has members of the same name. That's why they say you can not override but just shadow members in base classes when you define an equal named member in the derived class.
The same thing is true for non-virtual functions.
The only thing that behaves different is virtual functions. These functions can be overwritten in derived classes, and code that gets handed a pointer-to-base-class can virtual functions of the base class and end up executing implementations given in the derived class.
So the point of virtual functions is not to have the compiler reinterpret a certain function in the context of derived classes (as you seem to understand it), but to make it possible to replace a function in the base class by a different function in the derived class.
To go back to your motivation: If you want a display
function that prints a message that depends on the actual object type, the thing that is fixed is the printig, which doesn't need to be virtual. But you need a virtual function to obtain the object type. Like this:
#include <iostream>
#include <ostream>
class A {
public:
void display() { std::cout << get_message() << '\n'; }
virtual const char * get_message() { return "A instance"; }
};
class B : virtual public A {
public:
virtual const char * get_message() { return "B instance"; }
};
class C : virtual public A {
public:
virtual const char * get_message() { return "C instance"; }
};
class D : public B, public C {
public:
// virtual const char * get_message() { return B::get_message(); }
// virtual const char * get_message() { return C::get_message(); }
// virtual const char * get_message() { return "D instance"; }
};
int main(void)
{
D foo;
foo.display();
A* a_ptr = &foo;
a_ptr->display();
return 0;
}
The example as given will not compile (now this is due to the diamond pattern), because the compiler can not decide, what overrider of A::get_message()
, either B::get_message()
or C::get_message()
should be picked in D objects, you need to make one of the comments in D real code to declare the one-and-only get_message
for D, in which can re-use the existing functions.
Why can a derived class call base class constructor twice?
Drawing from link is misleading, it is not a diamond, but more a Y:
---------- ----------
| Person | | Person |
---------- ----------
^ ^
| |
---------- ----------
| Student | | Faculty |
---------- ----------
^ ^
| |
\----- ----/
\ /
|
----------
| TA |
----------
And yes you have two copies of member of Person
(Student::y
and Faculty::y
).
With virtual inheritance, you have a diamond with only one unique Person
.
Inheriting from multiple/diamond Inheritance
You need to make all your constructors public
and define a default constructor for A
because the string constructor will mark the default constructor as =delete
. Furthermore, the most derived class will initialize any virtual base class, quoting from the draft Standard:
12.6.2 Initializing bases and members [class.base.init]
10 In a non-delegating constructor, initialization proceeds in the
following order: — First, and only for the constructor of the most
derived class (1.8), virtual base classes are initialized in the order
they appear on a depth-first left-to-right traversal of the directed
acyclic graph of base classes, where “left-to-right” is the order of
appearance of the base classes in the derived class
base-specifier-list.
In this case that means that X
must indeed initalize A
.
#include <iostream>
#include <string>
class A
{
public:
A() { std::cout << "A\n"; }
A(std::string id) { std::cout << id << " A(id)\n"; }
};
class B : public virtual A
{
public:
B() { std::cout << "B\n"; }
};
class C : public virtual A
{
public:
C() { std::cout << "C\n"; }
};
class D : public B, public C
{
public:
D(std::string id): A(id) { std::cout << id << " D(id)\n"; }
};
class X : public D
{
public:
X(std::string id): A(id), D(id) { std::cout << id << " X(id)\n"; }
};
int main()
{
X x("bla");
x;
}
Extending a common base: Diamond inheritance vs. QObject
As indicated right upfront in my comment, I think this is a clear case for the Decorator Pattern if you want an easily extensible feature system, otherwise just inherit from QObject rather than from the base "interface" with pretty much the same code.
I will start with the IMHO worse approaches, supplied in the other answers given:
- Subclassing each spin box
This is obviously tiresome and even more important, you will not be able to support any QSpinBox subclass with those as you would always need to create a new subclass for each addition. It is simply an unflexible approach.
- Have a parent widget containing the button and spin box
This looks like an unnecessary coupling of two different things, and so you would not be able to reuse spin boxes easily should you trigger them any other way later than through buttons. I think the two concepts should remain distinct and separately managed.
Furthermore, dynamic_cast
ing is wrong as suggested as you should use qobject_cast
if any.
Let us have a closer look at the decorator approach:
This is not yet the solution for your case, but it shows pretty well how features can be added (i.e. "decorated") into existing hierarchies. To get a bit more concrete about your use case, let us see what would be what in your particular scenario:
Component: QAbstractSpinBox
Concrete components
- QSpinBox
- QDoubleSpinBox
- QDateTimeEdit
- QDateEdit
- QTimeEdit
Decorator: AbstractSpinBoxDecorator (this step can be left out in your case)
Concrete Decorator: RevertibleSpinBoxDecorator
Let us get our hands dirty with implementing this design:
main.cpp
#include <QAbstractSpinBox>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QDateTimeEdit>
#include <QDateEdit>
#include <QTimeEdit>
#include <QPushButton>
#include <QApplication>
#include <QMainWindow>
#include <QHBoxLayout>
#include <QWidget>
#include <QShowEvent>
class RevertibleSpinBoxDecorator : public QAbstractSpinBox
{
Q_OBJECT
public:
explicit RevertibleSpinBoxDecorator(QAbstractSpinBox *abstractSpinBox, QAbstractSpinBox *parent = Q_NULLPTR)
: QAbstractSpinBox(parent)
, m_abstractSpinBox(abstractSpinBox)
{
}
public slots:
void revert(bool)
{
QSpinBox *spinBox = qobject_cast<QSpinBox*>(m_abstractSpinBox);
if (spinBox) {
spinBox->setValue(spinBox->minimum());
return;
}
QDoubleSpinBox *doubleSpinBox = qobject_cast<QDoubleSpinBox*>(m_abstractSpinBox);
if (doubleSpinBox) {
doubleSpinBox->setValue(doubleSpinBox->minimum());
return;
}
QDateEdit *dateEdit = qobject_cast<QDateEdit*>(m_abstractSpinBox);
if (dateEdit) {
dateEdit->setDate(dateEdit->minimumDate());
return;
}
QTimeEdit *timeEdit = qobject_cast<QTimeEdit*>(m_abstractSpinBox);
if (timeEdit) {
timeEdit->setTime(timeEdit->minimumTime());
return;
}
QDateTimeEdit *dateTimeEdit = qobject_cast<QDateTimeEdit*>(m_abstractSpinBox);
if (dateTimeEdit) {
dateTimeEdit->setDateTime(dateTimeEdit->minimumDateTime());
return;
}
Q_ASSERT_X(false, "decorator", "concrete component unimplemented");
}
protected:
void showEvent(QShowEvent *event) Q_DECL_OVERRIDE
{
m_abstractSpinBox->show();
event->ignore();
hide();
}
private:
QAbstractSpinBox *m_abstractSpinBox;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = Q_NULLPTR) : QMainWindow(parent)
{
connect(pushButton, &QPushButton::clicked, revertibleSpinBoxDecorator, &RevertibleSpinBoxDecorator::revert);
QHBoxLayout *layout = new QHBoxLayout(centralWidget);
layout->addWidget(revertibleSpinBoxDecorator);
layout->addWidget(pushButton);
setCentralWidget(centralWidget);
}
private:
QWidget *centralWidget{new QWidget(this)};
QDoubleSpinBox *doubleSpinBox{new QDoubleSpinBox(this)};
RevertibleSpinBoxDecorator *revertibleSpinBoxDecorator{new RevertibleSpinBoxDecorator(doubleSpinBox)};
QPushButton *pushButton{new QPushButton(this)};
};
#include "main.moc"
int main(int argc, char **argv)
{
QApplication application(argc, argv);
MainWindow mainWindow;
mainWindow.show();
return application.exec();
}
If you want to get rid of the QAbstractSpinBox
inheritance, you will need to apply a bit more glue and IMHO for not much gain, while losing the flexibility. You would start off with something like this:
Non-Decorator
#include <QAbstractSpinBox>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QDateTimeEdit>
#include <QDateEdit>
#include <QTimeEdit>
#include <QPushButton>
#include <QApplication>
#include <QMainWindow>
#include <QHBoxLayout>
#include <QWidget>
#include <QShowEvent>
class RevertibleSpinBoxDecorator : public QObject
{
Q_OBJECT
public:
explicit RevertibleSpinBoxDecorator(QAbstractSpinBox *abstractSpinBox, QObject *parent = Q_NULLPTR)
: QObject(parent)
, m_abstractSpinBox(abstractSpinBox)
{
}
public slots:
void revert(bool)
{
QSpinBox *spinBox = qobject_cast<QSpinBox*>(m_abstractSpinBox);
if (spinBox) {
spinBox->setValue(spinBox->minimum());
return;
}
QDoubleSpinBox *doubleSpinBox = qobject_cast<QDoubleSpinBox*>(m_abstractSpinBox);
if (doubleSpinBox) {
doubleSpinBox->setValue(doubleSpinBox->minimum());
return;
}
QDateEdit *dateEdit = qobject_cast<QDateEdit*>(m_abstractSpinBox);
if (dateEdit) {
dateEdit->setDate(dateEdit->minimumDate());
return;
}
QTimeEdit *timeEdit = qobject_cast<QTimeEdit*>(m_abstractSpinBox);
if (timeEdit) {
timeEdit->setTime(timeEdit->minimumTime());
return;
}
QDateTimeEdit *dateTimeEdit = qobject_cast<QDateTimeEdit*>(m_abstractSpinBox);
if (dateTimeEdit) {
dateTimeEdit->setDateTime(dateTimeEdit->minimumDateTime());
return;
}
Q_ASSERT_X(false, "strategy", "strategy not implemented");
}
private:
QAbstractSpinBox *m_abstractSpinBox;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = Q_NULLPTR) : QMainWindow(parent)
{
connect(pushButton, &QPushButton::clicked, revertibleSpinBoxDecorator, &RevertibleSpinBoxDecorator::revert);
QHBoxLayout *layout = new QHBoxLayout(centralWidget);
layout->addWidget(doubleSpinBox);
layout->addWidget(pushButton);
setCentralWidget(centralWidget);
}
private:
QWidget *centralWidget{new QWidget(this)};
QDoubleSpinBox *doubleSpinBox{new QDoubleSpinBox(this)};
RevertibleSpinBoxDecorator *revertibleSpinBoxDecorator{new RevertibleSpinBoxDecorator(doubleSpinBox)};
QPushButton *pushButton{new QPushButton(this)};
};
#include "main.moc"
int main(int argc, char **argv)
{
QApplication application(argc, argv);
MainWindow mainWindow;
mainWindow.show();
return application.exec();
}
main.pro
TEMPLATE = app
TARGET = main
QT += widgets
CONIG += c++11
SOURCES += main.cpp
Build and Run
qmake && make && ./main
Related Topics
How Performing Multiple Matrix Multiplications in Cuda
Undefined Behavior Causing Time Travel
Why I Can Access Member Functions Even After the Object Was Deleted
C++ Error, Undefined Reference Class
Removing Watermark Out of an Image Using Opencv
Calculate Rolling/Moving Average in C++
Command Working in Terminal, But Not via Qprocess
Why Is Auto_Ptr Being Deprecated
What's the Differences Between .Dll , .Lib, .H Files
Write and Read String to Binary File C++
Detecting Simulated Keyboard/Mouse Input
How to Serialize Boost::Dynamic_Bitset
How to Install Feature Based on the Property Set in Custom Action
How Does C++ Preprocessors Work
Hooking Directx Endscene from an Injected Dll
What Is Different Between Join() and Detach() for Multi Threading in C++