在所有未来的派生类中,在抽象类构造方中执行初始化行为

发布于 2025-01-20 18:30:34 字数 2141 浏览 2 评论 0原文

我从 Qt 库中的现有输入字段类派生(不必熟悉它),在其中添加一个文本验证器来检查有效输入(使用 QLineEdit 验证选项)和一些进一步的功能/覆盖。

为此,我创建了一个名为 ConstrainedQLineEdit 的纯虚拟类,它派生自 QLineEdit,然后我从中实现特定的输入字段(例如 PasswordQLineEdit)。这是我的实现的简化

抽象基类:

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
};

密码的派生类

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;
  }
};

可执行文件

int main(int argc, char** argv)
{
    QApplication application(argc, argv);
    PasswordQLineEdit passwordQLineEdit;
    passwordQLineEdit.show();
    return application.exec();
}

执行后,我得到以下内容错误(没有调试器,应用程序会崩溃分段错误样式):

pure virtual method called
terminate called without an active exception

我的问题

我知道从基类构造函数调用虚函数是不受欢迎的,因为它可能会导致这种情况的问题。我也知道我可以将初始化放在派生类构造函数中。

但是,这是一个基类,我的团队将来将使用它来创建更多自定义字段,并且我想专门强制(或预定义)派生类的构造方式,以便其他团队成员实际上只具有定义一个传递相同参数的构造函数,然后通过 QString _getValidationInformation() 覆盖验证逻辑并完成它。

有什么设计建议可以在不破坏类结构的情况下实现此目的(我正在重写 QLineEdit 的更多虚拟函数并且还有其他逻辑)?

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)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

梦里°也失望 2025-01-27 18:30:34

我知道它是从
基类构造函数

不仅是“皱眉”,而且是错误的。编写这样的代码将导致错误。您的错误来自以下事实:在施工过程中,您的子类虚拟方法从未被调用,只有调用基类方法。因此,它试图从基类调用_getValidatorInformation(),但尚未实现,因此错误。如果您提供了默认实现,则编译器不会说什么,并且您将拥有广告错误的代码。

我建议使用工厂功能来创建对象。工厂功能将负责

  1. 创建对象。
  2. 验证先决条件。

一些伪代码以显示这个想法:

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;
}

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

  1. Create the object.
  2. Validate the preconditions.

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文