2012-01-26 55 views
10

我有一个类模板,其中一些方法被定义为虚拟的,以使我的类的用户能够在他的派生类中为他们提供实现。请注意,在我的模板类中,有一些使用虚拟类的非虚拟方法(应该返回值的虚拟类在非虚拟类中调用)。覆盖C++的虚拟方法

你可以给我一个正确的代码,其中父类的虚拟方法应该返回一个值(但它的实现是在一个子类中提供的)和父类中的虚方法返回的值的简单示例用于该类的其他方法。因为我看到某处(例如:Safely override C++ virtual functions),这可能会导致一些问题,用户定义的方法将会注意覆盖父类的虚方法。

注意:我使用g ++编译器对Code :: Blocks进行编程。

编辑:这里要求的我想要的一个简单的例子:)

template<typename T> 
class parent { 
public: 
    // Public methods that user can call 
    int getSomething(T t); 
    void putSomething(T t, int x); 

    // public method that user should implement in his code 
    virtual float compute(T t) { } 

    // protected or private methods and attributes used internally by putSomething ... 
    float doComplexeThings(...); // this can call 
}; 

的方法计算(应该由用户(子类)来实现。但是,此方法compute()由putSomething()和doComplexeThings()来调用。

+0

对于您所描述的内容,可能不需要虚拟方法(静态多态性可能适用于您) - 难以根据您的描述进行分析。请发布您写的示例代码,以便我们更清楚地知道您正在尝试做什么。 – kfmfe04

+0

链接问题中的示例是关于您认为是重写的内容,但实际上由于错误而声明了重载,例如,常量丢失,并不是关于返回值。你认为危险到底是什么? – CashCow

+0

在经典的模板代码中,您甚至不需要声明“虚拟浮点计算(T t)”。如果你有一个实例的句柄,你可以通过't.compute()'在putSomething()和doComplexeThings()中简单地使用它(调用它)。如果你的类T没有实现compute,编译器会报错。这样,父母和T实际上甚至不必生活在相同的继承层次中:即T是不需要父母关系的孩子。这样做也可以给你一个给父母一个更有意义的名字的机会(因为这是一种关系是不必要的)。 – kfmfe04

回答

18

您只需确保方法具有相同的签名(包括const/mutable修饰符和参数类型)。如果您无法覆盖子类中的函数,则可以使用纯虚拟定义来引发编译器错误。

class parent { 
public: 
    // pure virtual method must be provided in subclass 
    virtual void handle_event(int something) = 0; 
}; 

class child : public parent { 
public: 
    virtual void handle_event(int something) { 
    // new exciting code 
    } 
}; 

class incomplete_child : public parent { 
public: 
    virtual void handle_event(int something) const { 
    // does not override the pure virtual method 
    } 
}; 

int main() { 
    parent *p = new child(); 
    p->handle_event(1); // will call child::handle_event 
    parent *p = new incomplete_child(); // will not compile because handle_event 
             // was not correctly overridden 
} 
+0

*虚拟方法允许协变返回类型*。 –

+3

@als,只是'* covariant返回类型被允许*'的一个小细节,只有当函数返回一个引用或指针时才允许协变类型。我知道你知道这一点,但我将这个评论留给了其他人。 –

+2

@mschneider答案的问题在于,您只是为了避免潜在的问题而强制更改语义。在原来的情况下,该函数存在但可能超载,在您提出的解决方案中,它必须超载。这不会在重载现有函数时处理错误,而是强制派生类重载。 –

20

如果你可以使用你的编译器C++ 11层的功能则可以覆盖被标记为使与override特殊标识:

float compute() override; 

在派生类中,上面一行将导致编译器错误,因为该函数不覆盖基础中的成员函数(不正确的签名,缺少参数)。但请注意,这必须在每个派生类中完成,它不是您可以从基类强加的解决方案。

从基类你只能通过强制重写通过使函数纯虚拟,但是这改变了语义。它不是避免重写时的问题,而是部队覆盖所有情况。我会避免这种方法,如果你要遵循它,并且对于基类型有一个明智的实现,请使函数虚拟提供一个定义,以便派生类的实现可以将函数调用为基类型(即你强制执行,但在最简单的情况下,它只会将呼叫转发给父母)

+0

+1有趣的C++ 11功能 - 有助于确保您的方法真正重写某些内容 – kfmfe04

0

这个问题在2013年被问到。这是相当古老的,但我发现了一些新的东西,在答案中不存在。

我们需要认识的三个概念是超载覆盖,并隐藏

简短的回答,你想重载基类的继承函数。 但是,超载是为需要所有这些功能的函数添加多个行为的机制。但是虚拟函数显然在Base类中。

class A { 
public: 
    virtual void print() { 
    cout << id_ << std::endl; 
    } 
private: 
    string id_ = "A"; 
}; 

class B : A { 
public: 
    using A::print; 
    void print(string id) { 
    std::cout << id << std::endl; 
    } 
}; 

int main(int argc, char const *argv[]) { 
    /* code */ 
    A a; 
    a.print(); 
    B b; 
    b.print(); 
    b.print("B"); 
    return 0; 
} 

使用A ::打印添加;在你的派生类将做的工作!

虽然我不觉得这是一个好主意,因为在超载继承背后的理念是不同的,它可能不是一个好主意,窝在一起。