C++的虚函数与纯虚函数
C++项目看起来真的是太恼火了,源码阅读起来有点不便,幸亏有source insight 保命。C++有个很重要的特性,那就是多态。多态的实现主要是两种方式:一个就是函数的重载,另一个就是继承中对虚函数的重写。虚函数的引入对C++大型项目接口设计统一标准起到了非常重要的作用,与虚函数相对应的就是纯虚函数。
虚函数:用 virtual 标识标记的函数就是虚函数,只含有虚函数(且不含纯虚函数)的类就叫做虚类或者虚基类。虚函数必须要实现,可以是空函数体。被子类继承,子类不重写的话那就是直接基类的方法。
纯虚函数:同样含有 virtual 标识符,但是定义时函数则是在函数名后直接加 =0 定义;含有纯虚函数的类叫纯虚类,纯虚函数不用实现。子类只继承函数名,具体功能需要子类自己实现。
栗子奉上:
virtual void printTest(); //虚函数,需要函数体
virtual void printTest() = 0; //纯虚函数,只用定义
Java抽象类和接口
写过Java的人会发现,这个的功能特性与 java 的抽象类和接口的函数是类似的
抽象类:用 abstract 标识符标记的类就叫抽象类。抽象类的函数需要函数体,类似C++虚函数一样。
接口:与类(class)相对,用 Interface 标识符标记。接口只定义函数名,不具体实现。这个类似C++纯虚函数
直接栗子:
// 以下为多个文件
//-------------------- Base.java-----------------
package test;
abstract public class Base { // 基类,抽象类
public void printClass() { // 抽象类函数必须实现,不然会报错
System.out.println("print base class");
}
}
//------------------ Child1.java----------------
package test;
public class Child1 extends Base{ // 子类继承,可直接继承基类函数,不用实现
}
//------------------Child2.java----------------
package test;
public class Child2 extends Base {
@Override
public void printClass() { // 子类继承,可对基类函数进行重写
System.out.println("print child2 class");
}
}
//****************************************************************************
//------------------------------ IBase.java-------------------------
package test;
public interface IBase { //接口,只定义接口函数形式,不具体实现
void printClass();
}
//----------------------------- Child1_IBase.java----------------------
package test;
public class Child1_IBase implements IBase {
@Override
public void printClass() { // 实现接口类,函数必须重写
System.out.println("print Interface Child1");
}
}
//***************************************************************************
//------------------TestMain.java--------------
package test;
public class TestMain {
public static void main(String[] args) {
Child1 child1 = new Child1(); //未进行函数重写
Child2 child2 = new Child2(); //已进行函数重写
child1.printClass(); // 基类函数打印
child2.printClass(); // Child2 子类函数打印
Child1_IBase child1Interface = new Child1_IBase();
child1Interface.printClass();
}
}
虚类和纯虚类 虚函数和纯虚函数
话说回C++,C++的虚函数和纯虚函数的实现也就跟这 java 抽象类和接口差不多了。栗子二连
#include <iostream>
using namespace std;
class base { //虚基类
public:
virtual void printClass() { //虚函数实现
cout <<"print base class" <<endl;
}
};
class baseVirtual { //纯虚类,只要含有一个纯虚函数即是纯虚类
public:
virtual void printClass() { //虚函数,需要实现
cout <<"print baseVirtual class" <<endl;
}
virtual void printClassVirtual() = 0; //纯虚函数,不需实现
};
//---------------------------------------------------------------
class sameChild1: public base { //子类继承,不自己重写虚函数
};
class overrideChild2: public base{
public:
void printClass() override { // 虚函数重写,override 可不加上
cout <<"print child2 class" <<endl;
}
};
class virtualChild3 : public baseVirtual {
public:
void printClassVirtual() override { // 纯虚函数实现,override 可不加上
cout <<"print virtualChild3 class" <<endl;
}
};
//---------------------------------------------------------------
int main()
{
base baseIns; //虚基类,可以进行实例化
baseIns.printClass();
sameChild1 child1; // 子类,函数直接继承父类
child1.printClass();
overrideChild2 child2; // 子类,进行了函数重写
child2.printClass();
// baseVirtual baseVirIns; // 纯虚类,不可实例化
// baseVirIns.printClassVirtual();
virtualChild3 child3; // 纯虚类的子类,进行了纯虚函数的重写
child3.printClassVirtual();
return 0;
}
结果输出:
与java不同之处:
1. java 的抽象类和接口都是不可以单独实例化的,只能由子类实例化
2. C++只有纯虚类不可以实例化,普通的虚基类可以进行实例化;因为方法有实现(但其实Java抽象类也有实现,哈哈哈)。
同时既然说到了 override,就说一下我的理解:
C++ override的使用
首先需要明确一个问题,那就是子类到底从基类中继承到了什么,这里只讨论函数方法
1. 基类中的纯虚函数,子类仅继承接口形式,具体函数体需要自己实现。
2. 基类中的虚函数,子类继承到了接口形式+缺省实现。具体函数一般还是要子类自己实现,当然也可直接使用基类的。
3. 基类中的非虚函数,子类继承到接口形式+具体实现。具体函数一般不需要子类自己实现,当然你强行想再实现也是可以的。
那就好像有没有 override 都可以正常实现虚函数的重写,那为什么要加个 override 呢?其实 override 是C++ 11之后加入的保留字,用于对虚函数重写的强制检查。举个栗子,还是上面那个示例,
class overrideChild2: public base{
public:
/**
* 函数名写错!!!
* 假如没有 override ,程序员以为是对基类函数的重写,实际函数写错了。编译也不会有任何问题,到
* 函数调用的时候,仍然使用基类的接口形式调用;那么就会出现于预想中不一样的结果
**/
void printClassSSSP() /*override*/ {
cout <<"print child2 class" <<endl;
}
};
对基类虚函数重写的错误,有可能因为函数名,参数类型,返回类型等等这些产生错误;但是这个是书写错误,这个是不容易查找出来的。派生类只会把这个当成是一个全新的函数,这是会带来很大的困扰,于是就有了 override 。被override 标识的函数,就明确表示是对基类函数的重写,那么必然需要有存在这样一个基类函数,在编译过程就会进行一个强制检查。如果发现基类中并没有这个函数,那说明是我们写错了函数,这样会保证虚函数的有效重写。所以 override 针对对象是基类的虚函数,非虚函数表示基类一般希望子类继承自己的方法实现,加上一个 override 同样也会报错提醒。
override 使用前提:
1)子类想改写基类函数;
2)基类函数为虚函数;
3)函数形式与基类保持一致(包括函数名,参数,返回类型)
说白了 override 就是一种辅助的措施,防止一些不必要的错误
ps: 还是写这些杂的轻松快乐。磕盐要加糖,我们都一样。