方法在派生时应该始终是虚拟的吗

Should method be always virtual when derived?

本文关键字:虚拟 派生 方法      更新时间:2023-10-16

标题说明了一切(我不确定何时使用virtual关键字(。在这种情况下,函数SaySomething()应该是virtual吗?(或者在使用指针时可能只应该是virtual?(

#include <iostream>
class A{
    public:
        void SaySomething(){
            std::cout << "aaaaaa";
        }
};
class B : public A{
    public:
        void SaySomething(){
            std::cout << "bbbbbbb";
        }
};
int main(){
    A objectA;
    B objectB;
    objectA.SaySomething();
    std::cout << std::endl;
    objectB.SaySomething();
    return 0;
}

从技术上讲,在这种情况下,函数B::SaySomething不需要是virtual:编译器知道每个对象的运行时类型,因此在这两种情况下都会调用正确的方法。

然而,它与基类中的A::SaySomething同名,但没有覆盖它,这可能会误导代码的读者。如果您计划隐藏,而不是覆盖类B中的A::SaySomething,那么最佳实践是为成员函数赋予不同的名称。

这是一个重要的情况:

B b;
A &ab(b);
b.SaySomething();  // Calls B::SaySomething
ab.SaySomething(); // Calls A::SaySomething

更改引用同一对象的方式时的行为更改方法的行为不是代码读者所期望的。如果将A::SaySomething设为虚拟,则两个调用都会产生相同的行为。

virtual关键字是在C++中启用多态性的原因。您的示例严格来说是非多态的,因为方法调用解析是静态的。所以在你的例子中没有必要。

然而,如果你有。。。

class Shape {
  virtual double area() = 0;
}
class Circle : public Shape {
  virtual double area() {
    return PI * r * r;
  }
}
class Square : public Shape {
  virtual double area() {
    return h * w;
  }
}

代码。。。

Shape* shapes[2];
shapes[0] = new Circle(1);
shapes[1] = new Square(1, 1);
for (int i=0; i<2; i++)
    cout << "shape[" << i << "] area -> " << shapes[i]->area();

将打印圆形和方形的面积。。。

shape[0] area -> 3.1415926..
shape[1] area -> 1

如果您有:

class A {
    virtual void f() { cout << 1 << endl; }
};
class B : public A {
    virtual void f() { cout << 2 << endl; }
};

然后当你在这样的代码中使用它时:

A* a = new B();
a->f();

您的程序将写入2,因为您使用了virtual。它动态地解析所指向的对象是类型B,尽管指针是类型A。

如果不使用virtual,它将打印1并使用指针类型类中的函数。