在从给定超类继承的所有类上调用虚函数

Posted

技术标签:

【中文标题】在从给定超类继承的所有类上调用虚函数【英文标题】:Calling a virtual function on all classes inheriting from a given superclass 【发布时间】:2018-06-05 10:05:17 【问题描述】:

我正在尝试在我的 C++ 应用程序中实现用户默认设置。 为此,我创建了一个接口类,其中包含一个函数:

class IRegisterUserDefaults

public:
    IRegisterUserDefaults();
    virtual void registerUserDefaults()
;

从这个接口类继承的每个类都会实现注册用户默认值的功能。

到目前为止没有问题。但是最好的称呼方式是什么? 我来自 Objective-C,在那里我可以搜索所有类并找到实现接口并在它们上调用 registerUserDefaults 函数的类。我明白虽然 C++ 没有这种程度的内省。每个类调用一次函数就足够了(从而使其成为静态的)。

目标

如果类是 IRegisterUserDefaults 的子类,那么如果该函数可以“自动”调用,那就太好了。我尝试从 IRegisterUserDefaults 构造函数调用该方法,但看起来这并没有正确调用子类函数。有没有办法做到这一点?

另外,确保每个类只调用一次的最佳方法是什么?

【问题讨论】:

“很想听听你的想法!”征求意见是题外话,你有什么问题? 你为什么要这个?我的直觉是,这对于 c++ 来说不是一个好的设计选择,但是在不知道您要解决什么问题或默认注册应该做什么的情况下,很难说清楚。是默认运行时值吗? @user463035818 我想知道最好的解决方案是什么。最佳解决方案将仅通过继承 IRegisterUserDefaults 来调用适当的函数。 @super 我的用户默认值类似于用于绘制文本的默认字体。这需要在某个时候注册。我所追求的是一种方便的解决方案,因为我看到一个人只是忘记调用 registerUserDefaults 方法,所以我认为如果通过超类构造函数“自动”调用该方法将是理想的。 @Caleth 它不会,但是有办法找出存在哪些(Objective-C)类并实例化那些名称(例如)匹配某种模式或实现特定方法签名的类. 【参考方案1】:

IRegisterUserDefaults任何语言中都不是一个有意义的接口。

听起来您试图解决的实际问题是“运行一次代码,在课堂上或附近首次使用”。你可以用这样的东西来做到这一点

class HasUserDefaults 
    static std::once_flag register_once;
    void registerUserDefaults()  /*...*/ 
public:
    HasUserDefaults ()
     
      // in all the constructors
        std::call_once(register_once, &HasUserDefaults::registerUserDefaults, this);
    

    // other members
;

【讨论】:

那么registerUserDefaults() virtual 也没有任何意义。但是我的解决方案需要更少的样板文件,并且可以让您在一个地方收集所有必要的初始化。 HasUserDefaults::registerUserDefaultsbody 将靠近 HasUserDefaults 的其余部分,因此我不同意您的解决方案“让您将所有必要的初始化集中在一个地方" 这不是我阅读 OP 问题的方式。他想确保所有派生类都以某种方式“签入”,为此他需要对每个派生类进行操作,而不是(理想情况下)in 每一个。因此我的建议是:他可以去MakeDefaultsHandler<DerivedRegisterUserDefaults1> (); MakeDefaultsHandler<DerivedRegisterUserDefaults2> (); ...,都在一个地方。这些类本身将以 OP 选择的任何方式实现,我对此并不关心。无论如何,让我们停止猜测他真正想要什么,他需要说。 嗯,想到了。我的模板应该叫RegisterDefaultsHandler。这更好地描述了它的实际作用。编辑了我的答案,讨论一下很有用。 同意界面没有多大意义。正如我所说,我希望以一种自动注册类的方式实现它,并且子类化和实现该方法就足够了。但由于这不起作用,您的解决方案可能是最好的解决方案。不需要某种“注册表”,类可以自己处理用户默认注册。谢谢!【参考方案2】:

您是否有一个位置可以知道所有这些派生类?在这种情况下,在那里做:

// The type-list can be used in many ways, as you need
using all_classes = std::tuple<A, B, C, D /* and so on */>;

template <class... Ts>
static void register_all_classes(Y<Ts...>*)
 ((Ts().registerUserDefaults()), ...); 

register_all_classes((all_classes*)nullptr);

否则,你显然必须去中心化:

您是否有一个编译单元负责注册每个类?在这种情况下,请使用命名空间范围对象。也许为此使用帮助器:

template <class T>
struct Init 
    Init()  T().registerUserDefaults(); 
;

// Used in single TU as:
static Init<SomeRegisterUserDefaults> _;

否则,看看std::ios_base::Init&lt;iostream&gt;是如何做到的。我简化了,因为不需要指明 uninit:

template <class T>
struct MultiInit 
    MultiInit()  static Init<T> _; 
;

// Used in any number of TUs as:
static MultiInit<SomeRegisterUserDefaults> _;

【讨论】:

【参考方案3】:

这对你有用吗?

#include <iostream>
#include <string>

class IRegisterUserDefaults

public:
    IRegisterUserDefaults() 
    virtual void registerUserDefaults() = 0;
;

class MoreDerivedRegisterUserDefaults : public IRegisterUserDefaults

public:
    MoreDerivedRegisterUserDefaults (int x, int y) : m_x (x), m_y (y)  
    virtual void registerUserDefaults() override 
        std::cout << "MoreDerivedRegisterUserDefaults::registerUserDefaults called (" << m_x << ", " << m_y << ")" << std::endl;
    
private:
    int m_x, m_y;
;

template <class T, typename... Args> void RegisterDefaultsHandler (Args... args) 
    T obj (args...);
    obj.registerUserDefaults ();
    

int main ()

    RegisterDefaultsHandler<DerivedRegisterUserDefaults> ();
    RegisterDefaultsHandler<MoreDerivedRegisterUserDefaults> (1, 2);
    // ...

您必须在某处实例化每个派生类。

直播demo(更新)。输出:

DerivedRegisterUserDefaults::registerUserDefaults called
MoreDerivedRegisterUserDefaults::registerUserDefaults called (1, 2)

编辑:与@Caleth 交谈后,我稍微调整了代码以使我的意图更加清晰。

编辑 2: 已添加 Variadiac 模板,结果比我想象的要容易,有用的“操作方法”指南 here。

【讨论】:

好主意!这将很难实现,因为代码分布在多个框架中。没有像 main() 这样的中心位置知道 IRegisterUserDefaults 的所有子类。此外,您的代码意味着所有 IRegisterUserDefaults 子类的类都有一个没有任何参数的默认构造函数。情况并非总是如此。 好吧main()(或其他)只需要查看相关的头文件。如果你完善你的可变参数模板,你应该能够制作一个可以将任意参数列表传递给任何特定构造函数的模板。模板很擅长,我只是不够聪明,无法为您发布一个。如果你愿意,请给我投票。 TU 投票,为你添加了一个可变参数模板,因为我想了解它是如何完成的。结果:微不足道。【参考方案4】:

在子类构造函数中调用该方法,不能在基类构造函数中调用this,因为此时子类尚未构造。

【讨论】:

以上是关于在从给定超类继承的所有类上调用虚函数的主要内容,如果未能解决你的问题,请参考以下文章

使用纯多态性和继承从基类调用派生类上的函数而不进行强制转换?

虚继承与虚函数继承

虚继承和虚函数继承

4月14日多态的笔迹总结,

继承,虚继承机制

vc 父类实现一个虚函数,子类继承并也实现这个虚函数,子类调用这个虚函数,父类这个虚函数会不会执行?