(1)虚函数
在类的成员函数前面加 virtual 关键字,这个成员函数就是虚函数。
(2)虚函数重写
当在子类中定义了一个与父类完全相同的虚函数的时候,就称这个子类的函数重写(覆盖)了父类的这个虚函数。
(3)多态
当使用基类的指针或引用调用重写的虚函数是,当指向父类时,调用的函数就是父类的虚函数;当指向子类的时候,调用的函数就是子类的虚函数。也就是说,函数的参数不再与类型有关,而是跟对象有关。
例子如下:
#include<iostream>
using namespace std;
class Person{
public:
virtual void BuyTickets(){
cout << "买票" << endl;
}
};
class Student : public Person{
public:
virtual void BuyTickets(){
cout << "买半价票" << endl;
}
};
void Fun( Person& p){
p.BuyTickets();
}
int main()
{
Person p;
Student s;
Fun(p);
Fun(s);
return 0;
}
上面的代码中,Fun函数中形参是 Person的指针,通过这个指针调用了一个BuyTickets()函数。但是当main函数中两次调用Fun函数的时候,第一次传的是 Person对象,第二次传递的是Student对象。他们调用的函数分别是各自的BuyTickets()函数。见上图。这就是多态的意义,同一个函数名,传递不同的参数。
(4)重载和虚函数总结
- 构成重载,需要返回值、函数名、传参都一模一样。
- 去掉 Person 的 virtual 不再构成多态,这个时候,与对象无关,与类型有关。
- 去掉 Student 的 virtual ,保留 Person 的 virtual 。依然构成重载,因为 Student 默认继承 virtual。
- 重载有一个例外,协变。返回值可以是父子关系的指针或者引用。(见下文代码1)
- 只有成员函数才能定义为虚函数。
- 静态的成员函数不能定义为虚函数。因为没有this指针。
- 在类外面定义函数的时候,virtual只能加在声明,不能在类外的定义前加virtual。
- 构造函数不能的定义为虚函数。同时不要在构造函数和析构函数中调用虚函数,在这两个函数中,对象不是完整的,可能会出现,未定义的场景。
- 最好把基类的析构函数声明为虚函数。(见下文代码2)
//代码1、协变
class Person {
public:
//使用了父类的引用
virtual Person& BuyTickets(){
return this;
}
};
class Student : public Person{
public:
//使用了子类的引用
virtual Student& BuyTickets(){
return this;
}
};
//代码2、有一个场景,如果父类的指针指向了子类的对象。
class A{
public:
A(int a=10 )
:_pa( new int (a) )
{}
//~A(){
virtual ~A(){
delete _pa;
cout << "~A" << endl;
}
protected:
int *_pa;
};
class B : public A{
public:
B()
:A(10)
,_pb( new int(10) )
{}
~B(){
delete _pb;
cout << "~B()" << endl;
}
protected:
int *_pb;
};
int main()
{
//父类的指针pa指向了子类B对象
A* pa = new B;
delete pa;
pa = new A;
delete pa;
return 0;
}
此时~A()跟类型有关,调用了父类的析构,对象B的空间未释放,造成内存泄漏。
此时,virtual ~A()跟对象有关,调用了子类的析构函数,正确释放B的空间。
(5)纯虚函数
纯虚函数的格式就是在成员函数的括号之后 =0,他强制子类重写该函数。
其中包含了纯虚函数的类叫做抽象类(接口类)。抽象类不能实例化出对象,纯虚函数在派生类中重新定义之后,派生类可有实例化出对象。
class Person{
public:
//纯虚函数
virtual void Display() = 0;
protected:
string _name;
};
class Student{
public:
virtual void Display()
{}
};
//Person 对象不能存在,但是 Person的指针和引用是可以存在的
Person *p = new Student;
(6)继承体系同名成员函数关系
| 名称 | 作用域 | 要求 | 其他 |
|---|---|---|---|
| 重载 | 同一个作用域 | 函数名相同,形参不同 | 返回值可以不同 |
| 重写(覆盖) | 不同作用域 | 1、函数名、形参、返回值都相同(协变除外)2、基类函数必须要有virtual关键字 | 访问限定符无要求 |
| 重定义(隐藏) | 不同作用域 | 函数名相同 | 不同作用域,不是重写就是重定义 |
(7)继承与友元、静态成员
- 友元关系不能继承。
- 基类的友元函数,不能访问子类的保护、私有成员。
- 基类定义了一个讲台成员,整个继承体系中只能有一个,子类和父类共用。
本文详细介绍了C++中的虚函数、虚函数重写、多态的概念及其实现方式,并通过具体示例展示了如何利用虚函数实现多态。此外,还探讨了纯虚函数与抽象类的作用,以及在继承体系中同名成员函数之间的关系。
900

被折叠的 条评论
为什么被折叠?



