重写C++中的规则

Overriding rules in C++

本文关键字:规则 C++ 重写      更新时间:2023-10-16

从Java的角度来看,我惊讶地发现您只能重写具有virtual关键字的基方法。在Java中,您使用final关键字来声明一个方法不能被重写。

我脑子里有一个想法,你很少想禁止重写,这样有人就可以按照他们认为合适的方式扩展你的课程。

所以在C++中,如果你觉得有人可能想在某个阶段继承你的类(也许几年后有人认为这是一个很酷的想法),你会让你所有的方法都是虚拟的吗?

还是有一些我不知道的关键原因想要在C++中禁止这种行为?

作为参考,这是我用每种语言做的实验:

Java

    public class Base {
    void doSomething(){
    System.out.println("Doing the base thing");
    }
    }
    public class Derived extends Base {
    void doSomething(){
    System.out.println("Doing the derived thing");
    }
    public static void main(String... argv){
        Base object = new Derived();
        object.doSomething();
    }
    }

C++

    class Base
    {
    public:
        Base(void);
        ~Base(void);
        virtual void doSomething();
    };
    void Base::doSomething(){
        std::cout << "I'm doing the base thingn" << std::endl;
    }
    class Derived :
        public Base
    {
    public:
        Derived(void);
        ~Derived(void);
        void doSomething();
    };
    void Derived::doSomething(){
        std::cout << "I'm doing the dervied thing" << std::endl;
    }

    int main(void){
        Base * object = new Derived;
        object->doSomething();
        return 0;
    }

duffymo和Als正在引导您朝着正确的方向前进。我只是想评论一下你说的一件事:

所以在C++中,如果你觉得有人可能想在某个阶段继承从你的课堂上(也许几年后有人认为这是一个很酷的想法)做你把所有的方法都虚拟化了?

从软件工程的角度来看:如果您没有立即使用继承,并且没有使用接口,那么我不建议将您的方法声明为虚拟方法。

虚拟方法会带来非常轻微的性能下降。对于非关键代码路径,性能影响可能可以忽略不计。但是对于经常被调用的类方法,它可能会累加起来。编译器不能做那么多的内联和直接链接。相反,必须在运行时在v-table数组中查找要调用的虚拟方法。

当我的编码团队中有人在设计对话中说"以后某个时候可能会有人想要……"时,我的"防未来"反模式警报就会响起。设计可扩展性是一回事,但"面向未来的功能"应该推迟到那时。

此外,几年后,那个认为这是一个很酷的想法的人——让他自己将类方法转换为虚拟方法。无论如何,到那时你会从事更大的项目。:)

是的,在C++中,如果在基类中标记为virtual,则类方法只能是overidden

如果您的类是为继承而创建的,并且您的类方法旨在为Base和派生提供不同的行为,则将该方法标记为virtual

阅读良好:

何时将C++中的函数标记为虚拟函数

是的,您必须使所有方法都是虚拟的。

Java的立场是,默认情况下,一切都是公平的,禁止它需要采取行动。C++和C#则持相反的观点。

即使没有virtual,也可以覆盖基类中的方法。

以这个小程序为例:

#include <iostream>
struct A
{
    void m()
        { std::cout << "An"; }
    virtual void n()
        { std::cout << "An"; }
};
struct B : public A
{
    void m()
        { std::cout << "Bn"; }
    virtual void n()
        { std::cout << "Bn"; }
};
int main()
{
    A a;
    a.m();
    a.n();
    B b;
    b.m();
    b.n();
    A &c = b;
    c.m();
    c.n();
}

程序的输出为:

A.A.BBA.B

正如您所看到的,方法B::m覆盖了A中的相同方法。但只有当使用B的精确实例时,这才有效。在第三种情况下,当使用对基类的引用时,需要virtual才能使其工作。

来自以虚拟函数为默认值的语言的程序员往往会惊讶于C++对默认值的选择相反,即非虚拟函数是默认值。然而,请注意,[具体]虚拟功能的合同更难记录和测试,因为实际上涉及两个不同的合同:

  1. 所有覆盖功能的契约,即功能在概念上的作用
  2. 具体功能的契约

只记录其中一个并不能真正减少成本,因为另一半根本不清楚。这也意味着,您无法从特定的实现中查看实际的合同是什么(假设您处于文档由源提供的不那么典型的情况)。