C++多态(2) ——抽象类与final、override关键字

目录

一.抽象类 

        1.定义

        2.形式

3.举例:

解决方法:让子类重写纯虚函数,重写后子类就会变换为具体类,能够创建出对象了。

3.抽象类的作用

二.final与override关键字

        方法1:私有父类构造函数

方法2:私有父类的析构函数

2.1final关键字

        方法3:使用final关键字

final的真正用法:

2.2override关键字


一.抽象类 

        1.定义

       在有virtual修饰的虚函数的后面写上 = 0 ,则这个函数表示为纯虚函数。包含纯虚函数的类叫做抽象类,也叫做接口类,抽象类并不能实例化出对象,而且子类继承抽象父类后也不能实例化出对象。而解决方法:只有子类经过重写纯虚函数后,子类才能实例化出对象。纯虚函数规范了子类必须重写,另外纯虚函数更体现出了接口继承。

        

        2.形式

class <类名>{

public:

        virtual <类型><函数名>(<参数表>)=0; 

};

3.举例:

class Car {
public:
	virtual void Car_skill() = 0;
};

class Audi :public Car {

};

int main() {
	Car c;
	Audi a;
	return 0;
}

在上述代码中,新建了一个Car类,里面有一个虚函数,奇怪的是,这个虚函数设置=0了;继承Car类的子类Audi,和Car类在main函数中创建对象时,报错了。

        原因就是,Car类是抽象类,因为Car类中的Car_skill()函数是纯虚函数,虚函数是virtual,给虚函数加上=0就变成了纯虚函数了。


        根据概念可知:包含纯虚函数的类被称作是抽象类,抽象类是不能够实例化出对象的,而继承了Car类的子类Audi,Audi类也继承了父类的纯虚函数,那么Audi类也是抽象类,它也无法示例化出对象。

解决方法:让子类重写纯虚函数,重写后子类就会变换为具体类,能够创建出对象了。
class Car {
public:
	virtual void Car_skill() = 0;
};

class Audi :public Car {
	virtual void Car_skill() {
		cout << "加速性能好且操控感十足" << endl;
	}
};

int main() {
	//Car c;
	Audi a;		//此时子类Audi可以实例化出对象了,重写了从父类继承来的纯虚函数,那么Au'di也就不再是抽象类了
	return 0;
}

3.抽象类的作用

         虽然说抽象类不能定义对象, 但是可以定义指向抽象类数据的指针变量. 当子类成为具体类之后, 就可以用这种指针指向子类对象. 然后通过该指针调用虚函数, 实现多态性的操作。抽象类接口是面向对象程序设计中的核心概念, 是各种设计模式中必需的机制.

二.final与override关键字

在了解final关键字之前,我们先来聊聊一个类如何能不被子类所继承?

        其实,父类不让子类继承,最主要的办法就是封锁父类的构造函数,这样子类就算继承了父类,等到子类创建对象时,子类也无法调用父类的构造函数,子类也就无法给从父类那里继承来的成员变量赋值,实现了不被子类继承的目标。

        方法1:私有父类构造函数

class A {
private:
	A() {
	}
};

class B :public A {

};

int main() {
	B bb;
	return 0;
}

运行结果: 

方法2:私有父类的析构函数

class A {
public:
	A(int a)
	:_a(a) {
		cout << "A的构造函数" << endl;
	}

private:
	~A() {
		cout << "A的析构函数" << endl;
	}
public:
	int _a;
};

class B :public A {
public:
	B(int a=10, int b=5)
		:A(a)
		, _b(b) {
		cout << "B的构造函数" << endl;
			}
	
public:
	int _b;
};

int main() {
	B* ptr=new B;	//B类无法创建对象
	B b;			//这样,B类就无法使用A类继承过来的成员了
	//delete ptr;

	return 0;
}

        私有了父类的析构函数,即使子类对象能够调用父类的构造函数,也无法调用父类的析构函数,好比小说中的高燃台词:“假如你有能力创造这个强大的生命,但是你却无法永远的掌握它,也就没办法毁灭它,它的存在可能会威胁到我们,可能会导致生灵涂炭,世界毁灭,那么从一开始留不得它的存在!” 。编译器一开始就不会允许你这么做!所以该代码会报编译错误! 

 

还有第三种方式能够阻止子类继承父类。

2.1final关键字

        方法3:使用final关键字
	class A final{
	private:
		A()
		{}
	};
	

	class B : public A		// 无法从 "A" 继承,因为它已声明为 "final"	
	{};
	
	int main(){
		B bb;
		B* ptr = new B;
		return 0;
	}

使用final关键字修饰父类A,那么子类B就无法继承类A!报编译错误! 

final的真正用法:
//父类
class A {
public:
	 virtual void Sleep() final {		
		cout << "睡5个小时的觉" << endl;
	}
};

//子类
class B :public A {
	virtual  void Sleep() {
		cout << "睡5个小时的觉" << endl;
	}
};

在上边代码中,父类A的虚函数中加上了virtual关键字,在子类B继承了A类后,意味着该函数Sleep()不能被子类B重写,

2.2override关键字

class A {
public:
	virtual void Sleep() {
		cout << "睡5个小时的觉" << endl;
	}
};

//子类
class B :public A {
	  void Sleep(int i) override{
		cout << "睡5个小时的觉" << endl;
	}
};

override关键字的作用与final的相反,final是不想让子类重写父类的虚函数,而override是想让子类能够重写父类的虚函数,它的检查方式也是让系统进行检查

        如上图代码:A类中写了一个Sleep的虚函数,子类B中我想重写Sleep函数,加上了override,但是我写的Sleep函数并不是与父类A的Sleep完全相同的函数(函数参数不同),导致被override关键字检查出来了。

总结:

assert与final,override有异曲同工之妙,都是用来检查代码。

注:assert是在执行代码的过程中,由系统判断是否能够通过检查,检查报的是运行错误。


       而final,override是在编写代码的过程中就会让系统进行判断是否能通过检查,检查报的是编译错误:

        a. final的作用是让父类的函数不想要被子类重写,如果子类重写了该函数,则报错,该关键字用于放置在父类的函数定义末尾。

        b.override的作用是父类想让子类确保能够重写父类的函数,如果没有重写,则报错。该关键字用于放置在子类重写的虚函数定义末尾处。

猜你喜欢

转载自blog.csdn.net/weixin_69283129/article/details/132030885