目录
一.抽象类
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的作用是父类想让子类确保能够重写父类的函数,如果没有重写,则报错。该关键字用于放置在子类重写的虚函数定义末尾处。