2016-02-26 104 views
2

所以我最近不小心从基类的构造函数中调用了一些虚函数,即Calling virtual functions inside constructorsC++:构造派生类时自动运行函数

我意识到我不应该这样做,因为虚拟函数的覆盖不会被调用,但我怎样才能实现一些类似的功能?我的用例是我想要一个特定的函数在构造对象时运行,并且我不希望编写派生类的人不必担心它正在做什么(因为他们当然可以调用这个东西)他们的派生类构造函数)。但是,需要被调用的函数恰好会调用一个虚函数,我想让派生类能够覆盖它们是否需要。

但是因为一个虚拟函数被调用,所以我不能将这个函数粘贴到基类的构造函数中,并让它自动以这种方式运行。所以我似乎被卡住了。

有没有其他的方法来实现我想要的?我碰巧正在使用CRTP从基类访问派生类中的其他方法,我可以在构造函数中使用它而不是虚函数吗?或者现在的问题很多?我想也许它可以工作,如果被调用的函数是静态的?

EDIT2:也刚刚发现这个类似的问题:Call virtual method immediately after construction

+4

如果函数调用是构造派生类所必需的,并且是特定于该类的,那么听起来好像应该从派生类的构造函数中调用它。想要从一个技术上还不存在的对象调用方法并不是一个好主意。 –

+0

那么我想运行的函数是一种运行时验证程序,以确保它们写出的派生类符合某些要求。它需要访问虚拟功能以检查它们是否正常工作。 –

+0

在这种情况下,@ Jarod42提出的工厂方法可能是您最好的选择。 –

回答

2

如果真的需要,你可以访问工厂。

你可以这样做:

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

嗯,有趣,但可悲的是,没有工厂可以使用。 –

1

有没有简单的方法来做到这一点。一种选择是使用所谓的虚拟构造函数习惯用法,隐藏基类的所有构造函数,并暴露静态的'create' - 它将动态地创建一个对象,调用你的虚拟覆盖并返回(智能)指针。

这很难看,更重要的是,限制你动态创建的对象,这不是最好的。

但是,最好的解决方案是尽量少使用OOP。 C++强度(与流行的观点相反)在于它是非OOP特定的特征。考虑一下 - 标准库中唯一的多态类型是流,大家讨厌(因为它们是多态的!)

+0

我喜欢流:p。但是,嗯,我同意这个解决方案听起来像个坏主意......不知道如何重新考虑这个设计。 –

+0

@BenFarmer,你呢?他们的表现呢? – SergeyA

+0

我从来不用担心他们的速度。他们很慢? –

-1

似乎你想要这个,或需要更多细节。

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()"; 
    } 
}; 
0

我想要运行的特定功能每当对象被构造,[...它]在转恰好调用虚函数,我想允许派生类的能力如果他们想要,可以覆

这可以,如果你愿意住有两个限制,可以轻松完成:

  1. 建设者在整个类层次结构必须是非公开的,因而
  2. 工厂模板类必须用于构造派生类。

这里,“特定功能”是Base::check,虚拟功能是Base::method

首先,我们建立基类。它必须满足两个要求:

  1. 它必须与MakeBase,其检查类。我假设你想Base::check方法是私人的,只能在工厂使用。如果它是公开的,当然你不需要MakeBase
  2. 构造函数必须受到保护。

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< 
0

如果你看看其他人如何解决这个问题,你会注意到他们只是将调用初始化函数的责任传递给客户端。以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调用从BD::f?您将使用未初始化的指针,这很可能会导致崩溃。

...但我怎么能实现一些类似的功能?

如果你愿意违反规则,我猜可能有可能得到想要的虚拟表的地址并从构造函数调用虚函数。

相关问题