栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

[C++] 关于C++ virtual关键字的总结

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

[C++] 关于C++ virtual关键字的总结

关于C++ virtual关键字的总结

virtual关键字主要涉及下面几个方面的内容:

  • 动态绑定——在基类中,将派生类需要重新定义的函数声明为虚函数,这将有助于实现动态绑定。
    当基类的指针或引用指向子类的示例并调用某个成员函数时,若该成员函数定义为虚函数,程序将根据对象类型而不是指针或引用的类型来选择虚函数的版本(通过虚函数表vtbl实现);
    相反如果成员函数定义为非虚,则编译器对非虚函数的调用使用静态绑定,此时会在编译阶段就根据基类指针或引用的类型来选择调用的函数版本,于是就会调用基类的成员函数而不是子类的成员函数。
    如果某个方法在基类中定义为虚的,则在其所有派生链中的类中都是虚的。

  • 虚析构函数——应当总是把析构函数定义为虚的,除非确定某个类不会被用作基类。
    这样一来,当基类的指针或引用指向子类的示例时(如base* b=new Child),释放该实例时(delete b),调用基类的析构函数前才会先调用子类的析构函数。否则程序将只调用base的析构函数而不调用Child的析构函数,这可能导致内存泄漏等错误。

  • 构造函数不能是虚的——派生类不继承基类的构造函数!
    此外,在构造函数中调用虚函数,实际运行的是父类的相应函数。构造过程中多态是关闭的。

  • 友元不能是虚的——友元也不能继承!

  • 纯虚函数——当类中的某个成员函数被定义为纯虚函数时(如virtual void function2() = 0;),则这个类成为了抽象类,不能实例化对象。基类中的纯虚函数可以定义具体实现,但其具体实现不被子类所继承,若子类要想使用基类的实现必须显示地调用。
    析构函数可以是纯虚函数(如virtual ~base1() = 0;),但纯虚析构函数必须要定义其实现。一般用于没有其他成员函数可以定义为纯虚函数以使类成为抽象类的情况。

  • 派生类没有重新定义虚函数——如果派生类没有重新定义某个虚函数,则使用派生链中最新定义的虚函数版本。

  • 重新定义继承下来的虚函数并不是重载——对于子类中的虚函数,不管与基类同名虚函数的返回类型和参数列表是否相同,都会隐藏同名的基类虚函数。如下方代码中base类的virtual void function3() {}和Child类的virtual void function3(int n) {}在子类中并不会并存,base类的function3函数将被隐藏。
    因此,如果重新定义继承下来的方法,应确保该方法与基类中的原型完全相同(返回类型也要一致,这又称作调用特征标匹配);并且,如果基类的某个虚函数声明被重载,则子类应该重新定义所有同名的基类版本(像下面class Child2那样)。

  • 返回类型协变——上面基类方法被隐藏的情况,有一种例外:如果虚函数返回类型是基类的指针或引用,则可以将返回类型修改为派生类的引用或指针,并不会导致基类的同名方法被隐藏。这种特性被称为“返回类型协变”。如下方代码中base类的virtual base* clone() {}和Child2 类的virtual Child2* clone() {}。

  • 管理虚函数:override 和 final
    将虚说明符 override 放在虚函数的参数列表之后,这将显式地指出需要覆盖基类的哪一个虚函数(即注明哪些函数是需要重写),如果基类中没有特征标一致的虚函数,则编译器会报错。这可以避免重写过程中的疏忽大意导致上面提到的基类其他同名虚函数被隐藏的问题。如:virtual void function3(int n) override {}
    final 关键字可禁止派生类覆盖特定的虚函数。如:virtual void function3() final {}

  • 多重继承时使用虚基类——如果子类(class C : B1, B2 {})继承的两个父类都继承自相同的基类(class B1 : A {}; class B2 : A {}),则这个子类(C)会拥有两个基类副本(A)。
    通过在派生类声明中使用关键字virtual,使基类变为派生类的虚基类(class B1 : virtual A {}; class B2 : virtual A {}),然后再使用多重继承时子类就只会拥有基类的一个副本了。

class base
{
	base(){}
	virtual ~base(){}
	virtual void function1() {}
	virtual void function2() = 0; // pure virtual
	virtual void function3() {}
	virtual base* clone() {}
};

class Child : public base
{
	Child(){}
	virtual ~Child(){}
	virtual void function1() {}	// override
	virtual void function2() {};
	virtual void function3(int n) {}
};

class base1
{
	base1(){}
	virtual ~base1() = 0;	// pure virtual
	virtual function1() {}
};
base1::~base1() {}

class Child2 : public base
{
	Child2(){}
	virtual ~Child2(){}
	virtual void function1() {}	// override
	virtual void function2() {};
	virtual Child2* clone() {}
	// 重新定义所有function3
	virtual void function3() {}
	virtual void function3(int n) {}
};
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/347083.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号