基于赋值兼容的多态

多态–PolyMorphism


C++中的多态是指:由继承而产生的相关的不同的类,其对象对同一消息会作出不同的响应

例子:

甲乙丙三个班都是高二年级,他们有基本相同的属性和行为,在同时听到上课铃声的时候,他们会分别走向3个不同的教室,而不会走向同一个教室。

多态实现的前提:复制兼容

赋值兼容就是向上转型

派生类的对象引用指针可以赋值给基类,但是基类只能访问派生类从基类赋值的成员

复制兼容的三种情况:

  1. 派生类的对象可以赋值给基类对象
  2. 派生类的引用可以初始化基类的引用
  3. 派生类的指针可以初始化基类的指针

由于赋值操作发生在主程序,所以要访问基类成员,派生类必须公有继承。

#include <bits/stdc++.h>
using namespace std;
class Base{
public:
    Base(int x,int y):x(x),y(y){}
    Base(const Base& v):x(v.x),y(v.y){}
    Base& operator=(const Base& v){
        if(this==&v){
            return *this;
        }
        x = v.x;
        y = v.y;
        return *this;
    }
    void display() const{
        cout << x << "," << y << endl;
    }
protected:
    int x, y;
};
class Derived:public Base{//公有继承才能赋值重载
public:
    Derived(int x,int y,int z):Base(x,y),z(z){}
    Derived(const Base& b,int z):Base(b),z(z){}
    Derived(const Derived& m):Base(m),z(m.z){}
    Derived& operator=(const Derived& m){
        if(this==&m){
            return *this;
        }
        //调用基类operator=
        Base::operator=(m);
        z = m.z;
    }
    void display() const{//隐藏基类同名函数,无视参数和返回值
        Base::display();
        cout << z << endl;
    }
private:
    int z;
};
int main(){
    Base a(100, 200);
    Base b(a);
    Base c = a;
    Derived da(11, 22, 33);
    Derived db(a, 10);
    Derived dc(da);
    Derived dd = db;
    //三种运算符赋值重载
    a = dc;//派生类dc赋值基类a
    Base &m = db;//派生类引用赋值基类引用m
    Base *n = &da;//派生类指针赋值基类指针n
    //三种拷贝赋值重载
    Base x(dc);
    Base &y(db);
    Base *z(&dc);
    x.display();
    y.display();
    z->display();
}   

静态多态

函数重载就是一种静态多态,编译时期决定

void func(int a,double b){//编译:func_i_d
 cout << "a*b=" << a * b << endl;
}
void func(double a,double b){//编译:func_d_d
 cout << "a*b=" << a * b << endl;
}
int main(){
 int a = 4;
 double b = 2.4;
 double c = 3.1;
 func(a, b);
 func(b, c);
}   

但是函数重载不能仅仅返回值不同,只能是参数个数或者参数类型的不同

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

动态多态

动态多态不发生在编译时期,发生在运行时期

💖动态多态能发生的三个必要条件:

  • 基类中有虚函数
  • 派生类覆写基类的虚函数
  • 将派生类对象地址赋给基类的指针或引用,发生虚函数的调用

虚函数就是加上virtual声明,类内声明类外定义时候,类内声明加类外定义不加

覆写override,派生类实现虚函数就是覆写

类的定义

class shape{
public:
 shape(int x,int y):_x(x),_y(y){}
 //类内声明虚函数
 virtual void display() const;
protected:
 int _x, _y;
};
//virtual不能在类外定义
void shape::display() const{
 cout << _x << "," << _y << endl;
}
/*派生类1:圆类*/
class Circle:public shape{
public:
  Circle(int x,int y,int z):shape(x,y),radius(z){}
  virtual void display() const{//覆写
      cout << _x << "," << _y << ":" << radius << endl;
  }
private:
 int radius;
};
/*派生类2:矩形类*/
class Rect:public shape{
public:
 Rect(int x,int y,int a,int b):shape(x,y),length(a),width(b){}
 void display() const{//virtual可以不加
     cout << _x << "," << _y << ":" << length << "," << width << endl;
 }
public:
 int length, width;
};

主程序

用派生类赋值基类,基类实现相同的虚函数接口,但是实现的是派生类覆写的功能

int main(){
    Circle aa(100, 200, 300);
    Rect bb(13, 34, 4, 56);
    shape *ps = &aa;
    ps->display();//输出圆aa的display()
    ps = new Rect(9, 10, 11, 12);
    ps->display();//输出矩阵的display()
    shape &rf = bb;
    rf.display();//输出矩阵bb的display()
    rf = aa;
    rf.display();//输出圆aa的display()
    return 0;
}

小结:

  • 虚函数声明需要加上virtual,但是类外定义不要写

  • 派生类重新定义基类虚函数就叫覆写,函数要求返回值相同,参数类型和参数个数都相同

    也就是函数必须完全相同,同名同参同返回

  • 派生类覆写基类的虚函数,加不加virtual都行

  • 真正的多态只能指针才能实现(引用存在重新赋值的局限)

多个类覆写了虚函数,利用派生类赋值的基类指针调用函数依然指向派生类覆写的

class A{
public:
 virtual void display() {
     cout << "AAAAAA" << endl;
 }
};
class B:public A{

};
/*派生类2:矩形类*/
class C:public B{
public:
 void display() {//virtual可以不加
     cout << "CCCCCC" << endl;
 }
};
int main(){
 C().display();//CCCCCC
 C c;
 B *pb = &c;
 pb->display();//CCCCCC
 A *pa = &c;
 pa->display();//CCCCCC
 return 0;
}

区分:函数重载,隐藏和覆写

  • 函数重载:同名不同参,返回值不区分
  • 隐藏:父子类同名即覆盖,参数和返回值不区分
  • 覆写:子类重新定义父类虚函数,要求同名同参同返回

纯虚函数

virtual 函数声明=0;

纯虚函数没有实现体

含有纯虚函数的类叫抽象基类,抽象基类不可以实例化

作用就是被继承,给派生类提供接口

继承纯虚函数的类没有实现纯虚函数,依然不可以被实例化

#include <bits/stdc++.h>
using namespace std;
class Base{
public:
 Base(int a,int b):x(a),y(b){}
 //纯虚函数
 virtual void display() const = 0;
protected:
 int x, y;
};
class A:public Base{
public:
 A(int a,int b,int c):Base(a,b),val(c){}
 void display() const{
     cout << x << "," << y << "," << val << endl;
 }
private:
 int val;
};
class B:public Base{
public:
 B(int a,int b,char c):Base(a,b),ch(c){}
private:
 char ch;
};
int main(){
 A a(6, 7, 8);
 Base *ps = &a;
 ps->display();
 //B b(1, 2, 'z');报错,B没有实现纯虚函数定义,所以B也是纯虚函数
}

析构函数
  • 普通析构函数
class Animal{
public:
    Animal();
    ~Animal();
    virtual void voice() const = 0;
};
Animal::Animal(){
    cout << "call Animal()" << endl;
}
Animal::~Animal(){
    cout << "call ~Animal()" << endl;
}
//cat
class Cat:public Animal{
public:
    Cat() { cout << "call Cat()" << endl; }
    ~Cat() { cout << "call ~Cat()" << endl; }
    void voice() const{
        cout << "miaomiaomiao" << endl;
    }
};
//Dog
class Dog:public Animal{
public:
    Dog() { cout << "call Dog()" << endl; }
    ~Dog() { cout << "call ~Dog()" << endl; }
    virtual void voice() const{
        cout << "wangwangwang" << endl;
    }
};
  • 在栈上运行
int main(){
    Dog dog;
    Cat cat;
    Animal *ani = &cat;
    ani->voice();
    ani = &dog;
    ani->voice();
}
  • 运行结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

和以前一样:构造时候调用自己构造函数前会调用基类构造函数,运行结束,程序会自动先析构派生类对象再析构基类对象

在堆上分配内存

int main(){
    Animal *ani = new Dog;
    ani->voice();
    delete ani;
    Animal *tmp = new Cat();
    tmp->voice();
    delete tmp;
}

运行结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

发现析构只能虚构基类的,不能虚构派生类指针内存

虚析构:就是为了析构父类指针时候,可以完全析构子类对象

含有虚函数的类,析构函数也应该定义虚析构函数

class Animal{
public:
 Animal();
 virtual ~Animal();//虚析构
 virtual void voice() const = 0;
};
Animal::Animal(){
 cout << "call Animal()" << endl;
}
Animal::~Animal(){
 cout << "call ~Animal()" << endl;
}

基类在析构函数前加上virtual

主程序

int main(){
    Animal *ani = new Dog;
    ani->voice();
    delete ani;
    Animal *tmp = new Cat();
    tmp->voice();
    delete tmp;
}

运行结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

基类析构函数加上virtual后,delete基类指针就能析构派生类对象

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

虚函数的几个限制:

只有类的成员函数才能声明为虚函数

只有继承才涉及派生类覆写虚函数

静态成员函数不能是虚函数

因为静态成员函数是所有的,而实现多态必须有对象

内联函数不能是虚函数

虚函数是多态在运行时链接,但是内联函数是编译时候链接的

构造函数不能是虚函数

因为多态依赖于对象,但是构造函数没有创建对象

加virtual编译器不通过

当然析构函数可以加virtual,而且含有虚函数的类还必须加

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值