C++ Virtual关键字的具体使用

来自:网络
时间:2020-11-01
阅读:
免费资源网 - https://freexyz.cn/

基础理解和demo

普通的继承

#include<iostream>

class Parent {
public:
  void print() {
    std::cout << "Parent" << std::endl;
  }
};

class Child : Parent {
public:
  void print() {
    std::cout << "Child" << std::endl;
  }
};

int main() {
  Child c;
  c.print();
  return 0;
}

输出结果为 "Child"

但是如果是 "父类的指针指向子类的对象" 这种情况下, 使用这个父类的指针去调用被重写的方法呢, 结果会是什么呢? 从语法的本质上讲, 子类对象的内存前半部分就是父类, 因为可以将子类对象的指针直接转化为父类。

#include<iostream>

class Parent {
public:
  void print() {
    std::cout << "Parent" << std::endl;
  }
};

// 注意这里必须是 public Parent
// 不然会报错 cannot cast 'Child' to its private base class 'Parent'
class Child : public Parent {
public:
  void print() {
    std::cout << "Child" << std::endl;
  }
};

int main() {
  Parent* p = new Child();
  p->print();
  return 0;
}

这个时候输出的是 "Parent"  。

所以, 当一个成员函数需要被子类重写, 那么必须将其声明为virtual, 也就是 虚函数 , 注意, 子类覆写的方法的virtual关键字会自动继承而来, 可以显示地写或者不写(建议还是写上)。

这样修改完就没问题了:

#include<iostream>

class Parent {
public:
  virtual void print() {
    std::cout << "Parent" << std::endl;
  }
};

class Child : public Parent {
public:
  virtual void print() {
    std::cout << "Child" << std::endl;
  }
};

int main() {
  Parent* p = new Child();
  p->print();
  return 0;
}

加深理解

Virtual 关键字的一个重要概念 "只有在通过基类指针或引用间接指向派生类子类型时多态性才会起作用" , 也就是说, 基类的函数调用如果有virtual则根据多态性调用派生类的,如果没有virtual则是正常的静态函数调用,还是调用基类的。

举个例子

#include <iostream.h>
class Base
{
public:
virtual void f(float x){ cout << "Base::f(float) " << x << endl; }
void g(float x){ cout << "Base::g(float) " << x << endl; }
void h(float x){ cout << "Base::h(float) " << x << endl; }
};
 
class Derived : public Base
{
public:
virtual void f(float x){ cout << "Derived::f(float) " << x << endl; }
void g(int x){ cout << "Derived::g(int) " << x << endl; }
void h(float x){ cout << "Derived::h(float) " << x << endl; }
};
 
void main(void)
{
Derived d;
Base *pb = &d;
Derived *pd = &d;
// Good : behavior depends solely on type of the object
pb->f(3.14f); // Derived::f(float) 3.14
pd->f(3.14f); // Derived::f(float) 3.14
// Bad : behavior depends on type of the pointer
pb->g(3.14f); // Base::g(float) 3.14
pd->g(3.14f); // Derived::g(int) 3 (surprise!)
// Bad : behavior depends on type of the pointer
pb->h(3.14f); // Base::h(float) 3.14 (surprise!)
pd->h(3.14f); // Derived::h(float) 3.14
}

粘贴这个博客的一段话, 表达的就是这个意思:

bp 和dp 指向同一地址,按理说运行结果应该是相同的,而事实上运行结果不同,所以他把原因归结为C++的隐藏规则,其实这一观点是错的。决定bp和dp调用函数运行结果的不是他们指向的地址,而是他们的指针类型。 “只有在通过基类指针或引用间接指向派生类子类型时多态性才会起作用”(C++ Primer 3rd Edition)。pb是基类指针,pd是派生类指针,pd的所有函数调用都只是调用自己的函数,和多态性无关,所以pd的所有函数调用的结果都输出Derived::是完全正常的;pb的函数调用如果有virtual则根据多态性调用派生类的,如果没有virtual则是正常的静态函数调用,还是调用基类的,所以有virtual的f函数调用输出Derived::,其它两个没有virtual则还是输出Base::很正常啊 ,nothing surprise! 所以并没有所谓的隐藏规则,虽然《高质量C++/C 编程指南》是本很不错的书,可大家不要迷信哦。记住“只有在通过基类指针或引用间接指向派生类子类型时多态性才会起作用”。

免费资源网 - https://freexyz.cn/
返回顶部
顶部