Reputation: 363
I'm deriving from an existing input field class from the Qt Library (not necessary to be familiar with it), where I'm adding a text validator that checks for valid inputs (using the QLineEdit validation options) and some further functionalities/overrides.
For this I create a pure virtual class called ConstrainedQLineEdit
which derives from QLineEdit
, from which then I implement specific input fields (e.g. PasswordQLineEdit
). Here a simplification of my implementation:
Abstract Base class:
class ConstrainedQLineEdit : public QLineEdit
{
Q_OBJECT // QT macro that implements observer pattern with signals/slots among others (not relevant)
public:
virtual ~ConstrainedQLineEdit()
{
// delete validator;
}
protected:
ConstrainedQLineEdit(QWidget* parent = nullptr)
: QLineEdit(parent) // , validator(nullptr)
{
_initialize();
}
virtual void _initialize()
{
QString validatorInformation = _getValidatorInformation();
// validator = new Validator{validatorInformation};
// further logic
}
virtual QString _getValidatorInformation() const = 0;
// Validator validator;
// Further variables and necessary overrides of QLineEdit
};
Derived class for passwords
class PasswordQLineEdit : public ConstrainedQLineEdit
{
Q_OBJECT // idem
public:
PasswordQLineEdit(QWidget* parent = nullptr)
: ConstrainedQLineEdit(parent)
{}
virtual ~PasswordQLineEdit() = default;
protected:
QString _getValidatorInformation() const override
{
QString validatorInformation;
// logic generating the information
return validatorInformation;
}
};
Executable
int main(int argc, char** argv)
{
QApplication application(argc, argv);
PasswordQLineEdit passwordQLineEdit;
passwordQLineEdit.show();
return application.exec();
}
Upon executing I get the following error (without debugger the application crashes segmentation-fault style):
pure virtual method called
terminate called without an active exception
My question:
I'm aware that it is frowned upon to call a virtual function from the base class constructor since it can lead to exactly this kind of problems. I'm also aware I could just put the initialization in the derived-class constructor.
However, this is a base class that my team will use in the future to create further custom fields, and I want to specifically enforce (or pre-define) the behaviour of how derived classes are constructed so that other team-members really only have to define a constructor that passes the same arguments and then the validation logic via QString _getValidationInformation() override
and be done with it.
Any design recommendations to achieve this without breaking the class structure (I'm overriding further virtual functions of QLineEdit
and there's additional logic)?
Upvotes: 1
Views: 190
Reputation: 4296
I'm aware that it is frowned upon to call a virtual function from the base class constructor
It is not only "frowned upon", it is wrong. Writing such code will lead to bugs. Your error comes from the fact that your child class virtual method is never called, in the construction process, only the base class methods are called. Hence, it tries to call the _getValidatorInformation()
from the base class, but it has not been implemented, hence the error. If you had provided a default implementation, then the compiler would have said nothing and you would have ad bugged code.
What I would suggest is to use a factory function to create your object. The factory function would be responsible to
Some pseudo code to show the idea:
std::unique_ptr<ConstrainedQLineEdit> Create(EType type_)
{
std::unique_ptr<ConstrainedQLineEdit> lineEdit;
switch(type_)
{
case EType::Password:
lineEdit = std::make_unique<PasswordQLineEdit>();
// You can extend and add types here...
}
// At this point, construction is completed. You can use virtual methods!
lineEdit->Initialize();
return lineEdit;
}
Upvotes: 2