云计算百科
云计算领域专业知识百科平台

一文学会c++多态

文章目录

  • 多态的定义及实现
    • 构成多态两个条件
    • ⭐虚函数
    • 虚函数检查
  • 重载,重写,重定义区别
  • 抽象类
    • 实现继承和接口继承
  • 多态的原理🚩
    • 虚函数表
    • 原理
    • 静态绑定与动态绑定
    • 多继承

多态的定义及实现

坐公交时,爱心卡,学生卡,老人卡车票不一样价钱,这就是一种多态

多态在不同继承关系的类对象,调用同一函数,产生不同行为

构成多态两个条件

  • 必须是基类指针或引用调用虚函数
  • 派生类虚函数必须重写

#include <iostream>
using namespace std;
class person
{
public:
virtual void print()
{
cout << "person" << endl;
}
};
class student :public person
{
public:
virtual void print()
{
cout << "student" << endl;
}
};
void func(person& people)
{
people.print();
}
int main()
{
person p;
student s;
func(p);
func(s);
return 0;
}

person student

⭐虚函数

🚩前面加virtual就是虚函数 🚩虚函数重写(覆盖):派生类有一个跟基类相同的函数(返回类型,函数名字,参数列表),三相同,称子类虚函数重写了基类的虚函数

虚函数两个例外: 1,协变 基类和派生类虚函数返回类型不同。即基类函数返回基类指针或引用,派生类函数返回派生类指针或引用。

class person
{
public:
virtual person* test()
{
return new person;
}
};
class student :public person
{
public:
virtual student* test()
{
return new student;
}
};

2,析构函数 🚩析构函数默认构成多态,无论加不加virtual,编译器对析构函数名称特殊处理,处理后析构函数通称destructor

#include <iostream>
using namespace std;
class person
{
public:
virtual ~person()
{
cout << "~person()" << endl;
}
};
class student :public person
{
public:
virtual ~student()
{
cout << "~student()" << endl;
}
};
int main()
{
person* p1 = new person();
person* p2 = new student();
delete p1;
delete p2;
return 0;
}

~person() ~student() ~person()

虚函数检查

函数重写比较严格,有时函数名写错,编译是检查不出来的,运行后没得到预期结果才去找bug,c++11提供两个关键词==final,override,==来检查重写 1,final:虚函数不准被重写,否则编译不通过

class person
{
public:
virtual void A() final
{ }
};
class student :public person
{
public:
virtual void A()
{ }
};

2,override:检查函数是否重写了某个基类函数,没重写则编译报错

class person
{
public:
virtual void A()
{ }
};
class student : public person
{
public:
virtual void A() override
{ }
};

重载,重写,重定义区别

在这里插入图片描述

抽象类

在虚函数后面加上 =0 则为纯虚函数,包含纯虚函数的类叫抽象类,也叫接口类,抽象类不能实例化对象,派生类继承后也不能实例化对象,只有重写虚函数才能实例化对象。纯虚函数规范了派生类必须重写,更体现了接口继承。

class A
{
public:
virtual void print() = 0
{}
};
class B
{
public:
virtual void print()
{
cout << "B" << endl;
}
};

实现继承和接口继承

普通函数的继承是实现继承,派生类继承基类的函数,可以使用,继承的是函数的实现, 虚函数的继承是一种接口继承,派生类继承的是虚函数的接口,目的是实现重写,所以不实现多态,不要把普通函数定义为虚函数。

多态的原理🚩

虚函数表

求A类大小

#include <iostream>
using namespace std;
class A
{
virtual void func1()
{}
private:
int _a;
};
int main()
{
cout<<sizeof(A)<<endl;
return 0;
}

16 在这里插入图片描述 这里_vfptr是虚函数表指针,我是64位系统,指针8字节和整型4字节,对齐规则是16

🚩 一个含有虚函数的类都至少含有一个虚函数表指针(虚表),虚函数的地址要放进虚表中

接着分析虚表

#include <iostream>
using namespace std;
class A
{
public:
virtual void func1()
{}
virtual void func2()
{}
void func3()
{}
int _a;
};
class B :public A
{
public:
virtual void func1()
{ }
};
int main()
{
A a;
B b;
return 0;
}

在这里插入图片描述

结论: 🚩1,派生类有自己的虚表,内容继承基类,如果有重写(如func1)则覆盖基类虚表自己代替,如果派生类有新增的虚函数,则按照声明顺序依次放在虚表最后 2,func2(),是虚函数,继承下来放进虚表,func3(),是普通函数就没进 3,虚函数表本质是指针数组,一般情况下数组末尾放一个nullptr。

4,虚表存的是虚函数指针,虚函数和普通函数一样,都存在代码段 对象中存的是虚表指针,vs中虚表存在代码段

原理

class person
{
public:
virtual void print()
{
cout << "person" << endl;
}
};
class student :public person
{
public:
virtual void print()
{
cout << "student" << endl;
}
};
void func(person& people)
{
people.print();
}
int main()
{
person p;
student s;
func(p);
func(s);
return 0;
}

开篇的代码, 🚩当传p时,基类指针调用基类的虚函数,当传s时,基类指针调用派生类的虚函数, 这样就完成不同对象完成同一行为时,展现不同形态。

🚩满足多态的函数调用不是在编译时确定的,是在运行时到对象中现找的。不满足多态的函数编译时就确认好了。

静态绑定与动态绑定

静态绑定:又称前期绑定,在程序编译时就确认程序行为,也称静态多态,如函数重载 动态绑定:又称后期绑定,在程序运行时根据拿到的具体类型确定具体行为去调用函数,也称动态多态

多继承

多继承未重写的虚函数放在第一个虚表的末尾 在这里插入图片描述

赞(0)
未经允许不得转载:网硕互联帮助中心 » 一文学会c++多态
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!