C++:构造派生类时自动运行函数
C++ : Automatically run function when derived class is constructed
所以我最近不小心从基类的构造函数调用了一些虚函数,即在构造函数中调用虚函数。
我意识到我不应该这样做,因为不会调用虚拟函数的覆盖,但是我如何实现一些类似的功能?我的用例是,我希望每当构造对象时运行一个特定的函数,并且我不希望编写派生类的人不必担心这是在做什么(因为他们当然可以在派生类构造函数中调用这个东西)。但是,需要依次调用的函数恰好调用了一个虚函数,我希望允许派生类能够根据需要重写它。
但是因为虚拟函数被调用,我不能只是把这个函数放在基类的构造函数中,然后让它以这种方式自动运行。所以我似乎被卡住了。
有没有其他方法可以实现我想要的?
编辑:我碰巧正在使用 CRTP 从基类访问派生类中的其他方法,我可以使用它而不是构造函数中的虚函数吗?或者存在同样的问题吗?我想如果调用的函数是静态的,也许它可以工作?
edit2:也刚刚发现了这个类似的问题:构造后立即调用虚拟方法
如果确实需要,您可以访问工厂。
您可以执行以下操作:
template <typename Derived, typename ... Args>
std::unique_ptr<Derived> Make(Args&&... args)
{
auto derived = std::make_unique<Derived>(std::forward<Args>(args));
derived->init(); // virtual call
return derived;
}
没有简单的方法可以做到这一点。一种选择是使用所谓的虚拟构造函数习惯用法,隐藏基类的所有构造函数,而是公开静态"create" - 这将动态创建一个对象,对其调用虚拟覆盖并返回(智能)指针。
这是丑陋的,更重要的是,限制你动态创建的对象,这不是最好的事情。
但是,最好的解决方案是尽可能少地使用 OOP。C++优势(与流行的看法相反)在于它的非OOP特定特征。想想看 - 标准库中唯一的多态类家族是流,每个人都讨厌它(因为它们是多态的!
如果你看看其他人是如何解决这个问题的,你会发现他们只是将调用初始化函数的责任转移到了客户端。以MFC的CWnd
为例:你有构造函数,你有Create
,一个虚函数,你必须调用它才能有一个适当的CWnd
实例化:"这些是我的规则:构造,然后初始化;服从,否则你会惹上麻烦"。
的,它容易出错,但它比替代方案更好:"有人建议此规则是实现工件。事实并非如此。事实上,实现从构造函数调用虚拟函数的不安全规则要容易得多,就像从其他函数调用一样。但是,这意味着不能编写任何虚函数来依赖基类建立的不变量。那将是一个可怕的混乱。- 斯特劳斯特鲁普。我认为,他的意思是,将虚拟表指针设置为指向派生类的 VT 会更容易,而不是在构造函数调用从底层向下时继续将其更改为当前类的 VT。
我意识到我不应该这样做,因为不会调用虚拟函数的覆盖,...
假设对虚函数的调用将按照您想要的方式工作,由于不变量,您不应该这样做。
class B // written by you
{
public:
B() { f(); }
virtual void f() {}
};
class D : public B // written by client
{
int* p;
public:
D() : p( new int ) {}
void f() override { *p = 10; } // relies on correct initialization of p
};
int main()
{
D d;
return 0;
}
如果可以通过D
的VT从B
呼叫D::f
怎么办?您将使用未初始化的指针,这很可能导致崩溃。
。但是我怎样才能实现一些类似的功能呢?
如果你愿意打破规则,我想有可能获得所需虚拟表的地址并从构造函数调用虚拟函数。
我希望每当构造对象时运行一个特定的函数,[...它]反过来碰巧调用一个虚函数,我想允许派生类能够根据需要重写它。
如果您愿意忍受两个限制,这很容易完成:
- 整个类层次结构中的构造函数必须是非公共的,因此
- 必须使用工厂模板类来构造派生类。
这里,"特定函数"是Base::check
的,虚函数是Base::method
的。
首先,我们建立基类。它只需要满足两个要求:
- 它必须与它的跳棋类
MakeBase
成为朋友。我假设您希望Base::check
方法是私有的,并且只能由工厂使用。如果它是公开的,你当然不需要MakeBase
。 - 构造函数必须受到保护。
https://github.com/KubaO/stackoverflown/tree/master/questions/imbue-constructor-35658459
#include <iostream>
#include <utility>
#include <type_traits>
using namespace std;
class Base {
friend class MakeBase;
void check() {
cout << "check()" << endl;
method();
}
protected:
Base() { cout << "Base()" << endl; }
public:
virtual ~Base() {}
virtual void method() {}
};
模板化 CRTP 工厂派生自与Base
友好的基类,因此可以访问私有检查器方法;它还有权访问受保护的构造函数,以便构造任何派生类。
class MakeBase {
protected:
static void check(Base * b) { b->check(); }
};
如果您无意中在非派生自 Base
的类上使用它,则工厂类可能会发出可读的编译时错误消息:
template <class C> class Make : public C, MakeBase {
public:
template <typename... Args> Make(Args&&... args) : C(std::forward<Args>(args)...) {
static_assert(std::is_base_of<Base, C>::value,
"Make requires a class derived from Base");
check(this);
}
};
派生类必须具有受保护的构造函数:
class Derived : public Base {
int a;
protected:
Derived(int a) : a(a) { cout << "Derived() " << endl; }
void method() override { cout << ">" << a << "<" << endl; }
};
int main()
{
Make<Derived> d(3);
}
输出:
Base()
Derived()
check()
>3<
似乎你想要这个,或者需要更多详细信息。
class B
{
void templateMethod()
{
foo();
bar();
}
virtual void foo() = 0;
virtual void bar() = 0;
};
class D : public B
{
public:
D()
{
templateMethod();
}
virtual void foo()
{
cout << "D::foo()";
}
virtual void bar()
{
cout << "D::bar()";
}
};
为基类编写一个构造函数,该构造函数调用您想要的函数并继承构造函数
class Base {
public:
Base() {someFunc(); /* Rest of instructions the constructor should handle */}
protected:
// Protected stuff
private:
void someFunc() {/* Function Body */}
};
class Derived : Base {
public:
Derived() : Base() {/* Your derived constructor */}
// ...
};
(当然,这只有在基构造函数处理派生类的一般功能时才方便)
- 无法获取菜单选择以运行函数.C++
- 为什么std::async使用同一个线程运行函数
- MINGW - 正确运行函数所需的 cdecl
- 如何在 C 中使用空的 main() 方法运行函数?
- 运行函数作为 constexpr 和不作为 constexpr
- 如何在全局变量的构造函数之前运行函数
- 在 Cap'n Proto RPC 服务器中定期运行函数
- 为什么当我在 c++ 中运行函数时,我的代码显示数字 53
- CLang:在 std::thread 中运行函数会导致结构创建BAD_ACCESS
- 如何使用 TBB 在单个线程中运行函数
- 我在运行函数 GetVolumeInformation() 时得到非常随机的结果
- 如何使用 boost::asio io_service 运行函数异步
- Qt - 在其他线程上运行函数
- 在一组模板化对象上运行函数
- pybind11:属性错误:尝试从 py 文件运行函数时,模块'XXX'没有属性'YYY'
- 添加按钮以通知运行函数的通知
- 如何在程序后台运行函数(特别是自动保存函数)?QT / C++
- Qt - 在单独的线程中运行函数
- 如何在单独的线程上运行函数(如果线程可用)
- 如何发出编译和运行C++函数的 LLVM IR