基类指针只能调用基类的成员函数,不能调用派生类的成员函数
如果在基类的成员函数前加virtual关键字,把它声明为虚函数,基类指针就可以调用派生类的成员函数,通过派生类的成员函数还可以访问派生对象的成员变量。
有了虚函数,基类指针指向基类对象时就可以访问基类对象,指向派生类对象时就可以访问派生类成员函数,基类指针表现出了多种形式,这种现象称为多态。
基类引用也可以使用多态
C++
#include "main.h"
#include "Square.h"
#include "classB.h"
#include "string.h"
class base { // 基类
public:
string name = "base";
void printName() {
cout << "基类:" << "name: " << name << endl;
};
private:
int id = 0;
};
class genere :public base // 派生类继承基类
{
public:
int age = 20;
void printName() {
cout << "派生类:" << "name: " << name <<" age:" << age << endl;
}
private:
};
using namespace std;
int main() {
genere g;
g.printName();// 调用的派生类的printName函数
base* bp;
bp = &g;
bp->printName();// 调用的基类的printName函数
}
但是如果在基类的printName函数前加一个virtual关键字,派生类的printName函数就会重写派生类中的函数。
class base { // 基类
public:
string name = "base";
virtual void printName() { // 这里写成virtual再运行
cout << "基类:" << "name: " << name << endl;
};
private:
int id = 0;
};
#include "main.h"
#include "Square.h"
#include "classB.h"
#include "string.h"
class base { // 基类
public:
string name = "base";
virtual void printName() {
cout << "基类:" << "name: " << name << endl;
};
private:
int id = 0;
};
class genere :public base // 派生类继承基类
{
public:
int age = 20;
void printName() {
cout << "派生类:" << "name: " << name <<" age:" << age << endl;
}
private:
};
using namespace std;
int main() {
base b;
genere g;
// g.printName();// 调用的派生类的printName函数
base* bp;
bp = &b; // 指向基类对象,调用基类的函数
bp->printName();
bp = &g; // 指向派生类对象,调用派生类的函数
bp->printName();
}
- virtual关键字只能声明的时候写,在类外定义的时候不能加。
class base { // 基类
public:
string name = "base";
virtual void printName();
private:
int id = 0;
};
void base::printName() { // 正确写法
cout << "基类:" << "name: " << name << endl;
}
- 派生类中重定义虚函数时,函数特征需要相同,即使想写重载函数,在基类中必须也写上重载函数的虚函数。
- 当在基类中定义了虚函数时,如果派生类没有重定义该函数,那么将使用基类的虚函数
- 名字遮蔽和重载函数的规则也适用于虚函数
- 在派生类中重新定义基类的函数,则将它设置为虚函数;否则,不要设置为虚函数,第一效率会更高;第二编译器指出不要重新定义该函数
#include "main.h"
#include "Square.h"
#include "classB.h"
#include "string.h"
class heroBase { // 基类
public:
//string name = "hero";
virtual void skill(int skillNum) {
};
private:
int id = 0;
};
class xishi :public heroBase // 派生类继承基类
{
public:
xishi() {
name = "西施";
};
void skill(int skillNum) {
cout << name << " 释放了技能" << skillNum << endl;
};
private:
string name = "西施";
};
class hanxin :public heroBase // 派生类继承基类
{
public:
hanxin() {
name = "韩信";
};
void skill(int skillNum) {
cout << name << " 释放了技能" << skillNum << endl;
};
private:
string name = "韩信";
};
class libai :public heroBase // 派生类继承基类
{
public:
libai() {
name = "李白";
};
void skill(int skillNum) {
cout << name << " 释放了技能" << skillNum << endl;
};
private:
string name = "李白";
};
using namespace std;
int main() {
heroBase* hbptr = nullptr;
int heroId = 0;
cin >> heroId;
switch (heroId)
{
case 1: {
hbptr = new xishi;
break;
}
case 2: {
hbptr = new hanxin; break;
}
case 3: {
hbptr = new libai; break;
}
// ...
default: cout<<"请输入数字选择你的英雄"<skill(1);
hbptr->skill(2);
hbptr->skill(3);
delete hbptr;
}
}
通过用户输入数字来选择英雄,从而使用多态释放技能(执行派生类的函数)。
在执行完派生类的析构函数之后,会自动调用基类的析构函数
xishi 少了一个析构函数,会导致内存泄漏
解决方法:
把基类的虚函数前加一个virtual关键字即可
class heroBase { // 基类
public:
//string name = "hero";
virtual void skill(int skillNum) {
};
heroBase() {
cout << " heroBase构造函数" << endl;
};
virtual ~heroBase() {
cout << " heroBase析构函数" << endl;
}
private:
int id = 0;
};
class xishi :public heroBase // 派生类继承基类
{
public:
xishi() {
name = "西施";
cout << " xishi构造函数" << endl;
};
void skill(int skillNum) {
cout << name << " 释放了技能" << skillNum << endl;
};
~xishi() {
cout << " xishi析构函数" << endl;
};
private:
string name = "西施";
};
int main(){
heroBase* hb = new xishi;
delete hb;
}
- 纯虚函数
某些情况下,基类中不能对虚函数给出有意义的实现,把它声明为纯虚函数
语法:
`virtual 返回类型 函数名 (参数列表) = 0`
纯虚函数在基类为派生类保留一个函数的名字,以便派生类对它重新定义,如果在基类中没有保留函数名字,则无法支持多态性。
- 含有纯虚函数的类叫做抽象类,抽象类不能实例化对象,但是可以创建指针和引用,如果抽象类中没有任何一个纯虚函数,那么设置构造函数为纯虚函数即可。
dynamic_cast 运算符用于指向基类的指针来生成派生类的指针,它不能回答“==指针指向的是什么类的对象==”的问题,但是可以回答“==是否安全的将对象的地址赋值给特定类型的指针==”的问题。
语法:
`派生类指针 = dynamic_cast 返回对象的地址,如果失败,返回nullptr`
注意:
1. dynamic_cast 可以将派生类指针转换为基类指针,但是没有意义。
2. dynamic_cast 可以用于引用,但是,没有与空指针对应的引用值,如果转换请求不正确,会出现bad_cast异常
3. dynamic_cast 只适用于包含虚函数的类,执行的时候需要查看虚函数表
>typeid运算符和type_info类
int a = 0;
cout << "类名:" << typeid(a).name() << endl;
typeid 运算符用于获取数据类型的信息
- 语法一:typeid(数据类型)
- 语法二:typeid(变量名或表达式)
typeid运算符返回type_info类(在头文件
type_info类的实现随编译器而异,但至少有name()成员函数,该函数返回一个字符串,通常是类名。