文章目录
- 构成多态的条件
- 虚函数
- 重写
- [重载 重写 隐藏](#重载 重写 隐藏)
- 为什么析构函数要搞成符合多态?
- 原理
一个类如果是基类,它的析构函数最好加上virtual
构成多态的条件
- 必须通过基类的指针或者引用调用虚函数
- 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
多态,不同对象传递过去,调用不同函数
//// 多态调用看的指向的对象
//// 普通对象(普通调用(不构成多态)),看当前者类型
虚函数
只有成员函数才可以加virtual
加上virtual它就叫做虚函数
作用:完成重写
重写
虚函数重写的一些细节:
// 重写的条件本来是虚函数+三同(即派生类虚函数与基类虚函数的
返回值类型、函数名字、参数列表完全相同),但是有一些例外
// 1、派生类的重写虚函数可以不加virtual -- (建议大家都加上)
// 2、协变,返回的值可以不同,但是要求返回值必须是父子关系指针和引用
重载 重写 隐藏
函数重载发生在同一作用域
重写和隐藏 发生在基类和派生类
隐藏只要函数名一样就符合条件
为什么析构函数要搞成符合多态?
场景一
cpp
class Person
{
public:
~Person()
{
cout << "~Person()" << endl;
}
};
class Student:public Person
{
public:
~Student()
{
cout << "~Student()" << endl;
}
};
int main()
{
Person ps;
Student st;
return 0;
}
这种情况下程序走的好着呢
也没上多态,不是也可以吗?为什么非得把父类和子类的析构函数搞成虚函数重写呢?
因为下面的场景下,不构成重写会造成内存泄漏
cpp
class Person
{
public:
~Person()
{
cout << "~Person()" << endl;
}
};
class Student:public Person
{
public:
~Student()
{
cout << "~Student()" << endl;
delete[] _a;
}
private:
int* _a = new int[10];
};
int main()
{
/*Person ps;
Student st;*/
Person* p = new Person;//基类指针既可以指向基类,也可以指向派生类
delete p;
p = new Student;
delete p; //p->destructor() + operator delete(p)(free)
//p是基类指针,指向Student,多态条件一满足
//这里我们期望p指向谁,调用谁的析构
//如果析构函数不构成多态,那么p->destructor() 是普通调用,只看当前者p的类型,永远只调用~person
return 0;
}
运行结果
delete p 做了2件事
p->destructor() + operator delete§(free)
如果析构函数不构成多态,那么p->destructor() 是普通调用,只看当前者p的类型,永远只调用~person
这不是我们期望的,这里我们期望p指向谁,调用谁的析构
基类指针既可以指向基类,也可以指向派生类
那么我们就需要满足多态的条件
p是基类指针,指向Student,调用析构函数。多态条件一满足
编译器帮助我们把类析构函数都被处理成destructor这个统一的名字
形参类型一样,函数名一样,析构又没有返回值,那么就需加上virtual即可
cpp
class Person
{
public:
virtual ~Person()
{
cout << "~Person()" << endl;
}
};
class Student:public Person
{
public:
virtual ~Student()
{
cout << "~Student()" << endl;
delete[] _a;
}
private:
int* _a = new int[10];
};
int main()
{
/*Person ps;
Student st;*/
Person* p = new Person;//基类指针既可以指向基类,也可以指向派生类
delete p;
p = new Student;
delete p; //p->destructor() + operator delete(p)(free)
//p是基类指针,指向Student,多态条件一满足
//这里我们期望p指向谁,调用谁的析构
//如果析构函数不构成多态,那么p->destructor() 是普通调用,只看当前者p的类型,永远只调用~person
return 0;
}
原理
预热
Base的大小是多少呢?
那么虚函数存在哪里呢?
存在了代码段(常量区)
回顾我们类和对象大小时的知识,成员函数也不保存在对象中,而是存放在公共的代码段
这里的vfptr是虚函数表指针,存的只是虚函数的地址!
成员函数加了virtual就会放到虚函数表指针里面
对于基类指针或引用指向父类或者子类是如何调用不同的函数呢?
cpp
class Person {
public:
virtual void BuyTicket() { cout << "买票-全价" << endl; }
int _a = 1;
};
class Student : public Person {
public:
virtual void BuyTicket() { cout << "买票-半价" << endl; }
int _b = 1;
};
void Func(Person& p)
{
// 符合多态,运行时到指向对象的虚函数表中找调用函数的地址
p.BuyTicket();
}
int main()
{
Person Mike;
Func(Mike);
Student Johnson;
Func(Johnson);
return 0;
}
Mike和Johnson的监视中看到他们的虚函数表里面存的地址不同
P指针指向基类和指向子类时调用的函数是不同的地址
符合多态的话,P指向父类就是父类,P指向子类,完成切片后看到的还是一个子类,那么P到底是指向父类还是子类就不得而知了。
但是我不管,运行时,符合多态,运行时到指向对象的虚函数表中找调用函数的地址
在编译时就确定成员函数的地址
如果不符合多态,那么就看调用者P的类型,去Person里去找这个函数,在用函数名修饰规则就可以找到函数的地址