[英]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;
}
没有简单的方法可以做到这一点。 一种选择是使用所谓的虚拟构造函数idiom,隐藏基类的所有构造函数,而是暴露静态'create' - 它将动态创建一个对象,在其上调用虚拟覆盖并返回(智能)指针。
这很丑陋,更重要的是,限制你动态创建对象,这不是最好的事情。
但是,最好的解决方案是尽可能少地使用OOP。 C ++的强度(与流行的看法相反)在于它的非OOP特定特征。 想一想 - 标准库中唯一的多态classess系列是流,每个人都讨厌(因为它们是多态的!)
我希望每当构造一个对象时都会运行一个特定的函数,[...]反过来会调用一个虚函数,我想让派生类能够在需要时覆盖它。
如果你愿意忍受两个限制,这很容易做到:
这里,“特定函数”是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<
如果你看一下其他人如何解决这个问题,你会发现他们只是将调用初始化函数的责任转移到了客户端。 以MFC的CWnd
,比如:你有构造函数,你必须Create
,你必须调用有一个适当的虚函数CWnd
实例:“这是我的规则:构建,然后初始化; 服从,否则你会遇到麻烦“ 。
是的,它容易出错,但它比替代方案更好: “有人建议此规则是一个实现工件。 不是这样。 实际上,实现从构造函数调用虚函数的不安全规则与从其他函数完全相同,会明显更容易。 但是,这意味着不能编写任何虚函数来依赖基类建立的不变量。 那将是一个糟糕的混乱。“ - Stroustrup 。 我认为,他的意思是,将虚拟表指针设置为指向派生类的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
怎么办? 您将使用未初始化的指针,这很可能会导致崩溃。
...但我怎样才能实现一些类似的功能呢?
如果你愿意违反规则,我想有可能获得所需虚拟表的地址并从构造函数中调用虚函数。
似乎你想要这个,或者需要更多细节。
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()";
}
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.