佛山做网站制作公司,石狮seo,老网站权重低的原因,宿迁网络推广公司#x1f4e2;博客主页#xff1a;https://blog.csdn.net/weixin_43197380#x1f4e2;欢迎点赞 #x1f44d; 收藏 ⭐留言 #x1f4dd; 如有错误敬请指正#xff01;#x1f4e2;本文由 Loewen丶原创#xff0c;首发于 CSDN#xff0c;转载注明出处#x1f649;博客主页https://blog.csdn.net/weixin_43197380欢迎点赞 收藏 ⭐留言 如有错误敬请指正本文由 Loewen丶原创首发于 CSDN转载注明出处现在的付出都会是一种沉淀只为让你成为更好的人✨ 文章预览 一. 虚函数(virtual)二. 虚函数中的关键字三. 纯虚函数四*. 基类的析构函数务必写成虚函数虚析构函数五. 总结 一. 虚函数(virtual)
定义在某基类中的成员函数
成员函数声明基类中为 virtual开头该成员函数在一个或多个子类派生类中被重新声明、定义
格式virtual 函数返回类型 函数名 ( 参数表 ) { 函数体 }
目的通过指向派生类的基类指针或引用访问派生类中同名覆盖成员函数实现多态性。
例如 Human *phumen new Men(); //可通过基类Human的指针phumen调用子类中的同名函数实现多态
多态性
顾名思义就是“多个性态”。更具体一点的就是用一个名字定义多个函数这些函数执行不同但相似的工作。最简单的多态性的实现方式就是函数重载和模板这两种属于静态多态性。还有一种是动态多态性其实现方式就是我们今天要说的虚函数。
下面来看一段简单的代码
class Human
{
public:void print() { cout This is 人类 endl; }
};class Men :public Human
{
public:void print() { cout This is 男人 endl; }
};int main()
{ Human human;Men men;human.print();men.print();
}通过class Human和class Men的print()这个接口输出的结果也是我们预料中的分别是This is 人类和This is 男人。
但这是否真正做到了多态性呢
No多态还有个关键之处就是一切用指向派生类的基类指针或引用来操作对象。那现在就把main()处的代码改一改。
int main()
{ //Human human;//Men men;//human.print();//men.print();Human * phuman new Human;Human * phuman1 new Men;phuman-print();phuman1-print();
}可以看出父类指针phuman1明明指向的是子类class Men对象但却是调用的父类class Human的print()函数这不是我们所期望的结果。
那么解决这个问题即通过一个父类指针或对象调用所有子类中的成员函数或变量就需要用到虚函数
class Human
{
public:virtual void print() { cout This is 人类 endl; } //现在成了虚函数了
};class Men :public Human
{
public:virtual void print() { cout This is 男人 endl; } //这里需要在前面加上关键字virtual吗
};现在重新运行main的代码这样输出的结果就是This is 人类和This is 男人。 毫无疑问class A的成员函数print()已经成了虚函数那么class B的print()成了虚函数了吗
回答是Yes我们只需在把基类的成员函数声明前加virtual其派生类的相应的同名同参成员函数也会自动变为虚函数。所以class B的print()也成了虚函数。对于在派生类的相应函数前是否需要用virtual关键字修饰 看个人编程习惯。
总结指向基类的指针在操作它的多态类对象时会根据不同的派生类对象调用其相应的函数这个函数就是虚函数。 二. 虚函数中的关键字
override关键字
为了避免在子类中写错虚函数没有和基类的成员函数同名同参在C11中可以在子类虚函数声明后增加一个关键字 override 。
注意override关键字用在子类中而且是虚函数专用用了这个关键字后编译器会认为子类的虚函数覆盖了基类中的同名函数那么编译器就会在父类中找同名同参的虚函数如果没找到编译器就会报错。这样如果不小心在子类中把虚函数名称或参数写错了编译器会帮助纠错。
final关键字
final关键字也是虚函数专用但是是用在父类中的作用是在父类的函数声明中加了final那么任何尝试覆盖该函数的操作都将引发错误。 三. 纯虚函数
定义 纯虚函数是在①基类中声明的虚函数但它在基类中②没有定义但③要求任何派生类都要定义自己的实现方法。
格式 在基类中实现纯虚函数的方法是在函数原型后加“0”
virtual void funtion1() 0; //纯虚函数在基类中定义没有函数体只有一个函数声明抽象类由来一旦基类中有纯虚函数那么则不能生成这个类的对象这个了就成为了“抽象类”。
抽象类目的用来统一管理子类对象。
Human human; //不合法
Human *phuman new Human; //不合法核心两点总结
含有纯虚函数的类叫抽象类抽象类不能生成该类对象主要用于当做基类来生成子类用的子类中必须要实现该基类中定义的纯虚函数
问题我们知道纯虚函数在基类中没有定义那么虚函数在基类中一定要定义实现吗
class Location
{
public:Location(){}~Location(){}public:virtual bool Check(); // 这里一定要实现吗
};class LineLocation : public Location
{
public:LineLocation(){}~LineLocation(){}public:virtual bool Check() {return 1;}
};int _tmain(int argc, _TCHAR* argv[])
{Location* loc NULL;loc new LineLocation();bool b loc-Check();return 0;
}回答 虚函数在基类中一定要实现如果基类中的虚函数不想实现只想通过派生类来实现需要将基类中的虚函数换成纯虚函数(0)。因为虚函数的地址在链接的时候需要放到类的虚函数表中所以即使你的代码里面没有调用这个函数编译器也需要取它的地址已经有对它的引用了就必须要实现才行。
注因为纯虚函数就相当于接口无法实例化即Location loc;编译是不能通过的。即有纯虚函数的类将其作为参数也好另一个类的成员变量也好只能将其定义为指针或引用只要不给基类实例化对象就行。 四*. 基类的析构函数务必写成虚函数虚析构函数
基类中的虚拟成员希望其派生类定义自己的版本。特别是基类通常应该定义一个虚拟析构函数即使它不起作用析构函数必须是虚拟的以允许动态分配和销毁继承层次结构中的对象。
那么为什么析构函数必须是虚拟的而我们新建程序时默认的析构函数却不是虚拟的呢
1、为什么析构函数必须是虚拟的 因为指针指向的是一个派生类实例我们销毁这个实例时肯定是希望先清理派生类自己的资源同时又清理从基类继承过来的资源。而当基类的析构函数为非虚函数时删除一个基类指针指向的派生类实例时只清理了派生类从基类继承过来的资源而派生类自己独有的资源却没有被清理。
总结如果一个类想要做基类被其他类继承那么我们必须定义这个类的析构函数并且还要将其写成虚函数普通类可不定义析构函数为虚函数或直接不写析构函数。这样在delete释放指向的派生类实例的基类指针时清理工作才能全面进行才不会发生内存泄漏。
2、为什么默认的析构函数不是虚函数
虚函数不同于普通成员函数当类中有虚成员函数时类会自动进行一些额外工作。这些额外的工作包括生成虚函数表和虚表指针虚表指针指向虚函数表。每个类都有自己的虚函数表虚函数表的作用就是保存本类中虚函数的地址我们可以把虚函数表形象地看成一个数组这个数组的每个元素存放的就是各个虚函数的地址。 这样一来就会占用额外的内存当们定义的类不被其他类继承时这种内存开销无疑是浪费的。
这样一说问题就不言而喻了。当我们创建一个类时系统默认我们不会将该类作为基类所以就将默认的析构函数定义成非虚函数这样就不会占用额外的内存空间。同时系统也相信程序开发者在定义一个基类时会显示地将基类的析构函数定义成虚函数此时该类才会维护虚函数表和虚表指针。
参考博文为什么析构函数必须是虚函数为什么默认的析构函数不是虚函数 五. 总结
1、定义纯虚函数是为了实现一个接口起到一个规范的作用规范继承这个类的程序员必须实现这个函数。 2、虚函数必须实现如果不实现编译器将报错。 3、调用虚函数执行的是“动态绑定”。动态表示的就是在我们程序运行的时候才能知道调用了哪个子类的虚函数。 4、实现了纯虚函数的子类该纯虚函数在子类中就编程了虚函数子类的子类即孙子类可以覆盖该虚函数由多态方式调用的时候动态绑定。 5、虚函数是C中用于实现多态的机制。核心理念就是通过基类访问派生类定义的函数。 6、在有动态分配堆上内存的时候析构函数必须是虚函数但没有必要是纯虚的。 7、友元不是成员函数只有成员函数才可以是虚拟的因此友元不能是虚拟函数。但可以通过让友元函数调用虚拟成员函数来解决友元的虚拟问题。 8、析构函数应当是虚函数将调用相应对象类型的析构函数因此如果指针指向的是子类对象将调用子类的析构函数然后自动调用基类的析构函数。 下雨天最惬意的事莫过于躺在床上静静听雨雨中入眠连梦里也长出青苔。