C++学习笔记(十七):多态

分类

静态多态:函数重载和运算符重载,编译阶段确定函数地址(地址早绑定)
动态多态:派生类和虚函数实现运行时多态,运行阶段确定函数地址(地址晚绑定)

动态多态

地址早绑定:

class A{
public:
    void test(){
        cout<<"A"<<endl;
    }
};

class aSon:public A{
public:
    void test(){
        cout<<"aSon"<<endl;
    }
};

void test1(A &a){
    a.test();
}
.
.
.
aSon s;
test1(s);

因为test1是地址早绑定,所以在编译时就确定了test1会调用父类的test函数。在调用test1时将子类类型转换为了父类。
地址晚绑定:

class Base{
public:
	//虚函数
    virtual void test(){
        cout<<"Base"<<endl;
    }
};

class Son:public Base{
public:
    virtual void test(){
        cout<<"Son"<<endl;
    }
};

class secSon:public Base{
public:
	//子类的virtual可以省略
    virtual void test(){
        cout<<"secSon"<<endl;
    }
};

void test0(Base &base){
    base.test();
}
.
.
.
Son s;
test0(s);
secSon sec;
test0(sec);

此时test0是地址晚绑定,在运行时确定函数地址,当传入的是Son时,调用Son的test函数,当传入的是secSon时,调用secSon的test函数。

满足条件
  1. 有继承关系
  2. 子类要重写父类的虚函数
    重写:子类函数名、参数列表和返回值要与父类相同
    重载:函数名相同,参数列表必须有所不同
调用方式

父类的指针或者引用执行子类对象

实质

当一个父类有一个虚函数时,他的内存空间就增加了一个虚函数指针vfptr,这个指针指向一个虚函数表格vftable,这个表格记录了所属父类的虚函数的地址。
当一个子类继承了这样一个父类时,他会继承其虚函数指针,并将虚函数表中的地址换成其重写的函数的地址。
使用父类的指针或者引用执行子类对象时,就会调用子类自己重写的函数。

存在的问题

父类指针在析构时,不会调用子类中的析构函数,导致子类如果有堆区属性,会出现内存泄漏的问题。

class Base{
public:
    Base(){
        cout<<"Base构造函数"<<endl;
    }
    ~Base(){
        cout<<"Base析构函数"<<endl;
    }
    virtual void test()=0;
};

class Son:public Base{
public:
    string name;
    int *age;
    Son(string _name,int _age){
        name=_name;
        age=new int(_age);
        cout<<"Son构造函数"<<endl;
    }
    void test(){
        cout<<name<<"--"<<*age<<endl;
    }
    ~Son(){
        cout<<"Son析构函数"<<endl;
        if(age!=NULL){
            delete age;
            age=NULL;
        }
    }
};
int main(){
    Base *b=new Son("killer",23);
    delete b;
    return 0;
}

运行结果:
在这里插入图片描述

解决方法
将父类中的析构函数写成虚析构。

class Base{
public:
    Base(){
        cout<<"Base构造函数"<<endl;
    }
    //虚析构
    virtual ~Base(){
        cout<<"Base析构函数"<<endl;
    }
    virtual void test()=0;
};

class Son:public Base{
public:
    string name;
    int *age;
    Son(string _name,int _age){
        name=_name;
        age=new int(_age);
        cout<<"Son构造函数"<<endl;
    }
    void test(){
        cout<<name<<"--"<<*age<<endl;
    }
    ~Son(){
        cout<<"Son析构函数"<<endl;
        if(age!=NULL){
            delete age;
            age=NULL;
        }
    }
};
int main(){
    Base *b=new Son("killer",23);
    delete b;
    return 0;
}
意义
  1. 组织结构清晰
  2. 可读性强
  3. 利于前期和后期的扩展和维护

抽象类

有纯虚函数的类叫抽象类,有纯虚析构的类也属于抽象类

纯虚函数

语法:virtual 返回值类型 函数名 (参数列表)= 0 ;

抽象类特点
  1. 无法实例化
  2. 抽象类的子类必须重写抽象类中的纯虚函数,否则依然是抽象类/font>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值