C++的多态

>基本概念

基类指针只能调用基类的成员函数,不能调用派生类的成员函数
如果在基类的成员函数前加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<<"请输入数字选择你的英雄"<<endl;
        break;
    }
    if (hbptr != nullptr)
    {
        hbptr->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 运算符用于指向基类的指针来生成派生类的指针,它不能回答“==指针指向的是什么类的对象==”的问题,但是可以回答“==是否安全的将对象的地址赋值给特定类型的指针==”的问题。
语法:
`派生类指针 = 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类(在头文件<typeinfo>中定义)的对象的引用
type_info类的实现随编译器而异,但至少有name()成员函数,该函数返回一个字符串,通常是类名。

  • 22
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值