C++ Note 继承指向 (多态)
引自:https://blog.csdn.net/baidu_35679960/article/details/80812527
引自:https://blog.csdn.net/qq_21989927/article/details/111226696
引自:https://www.runoob.com/cplusplus/cpp-polymorphism.html
为什么父类指针可以指向子类实例:
可以通俗的理解,子类可能含有一些父类没有的成员变量或者方法函数,但是子类肯定继承了父类所有的成员变量和方法函数。
因为父类有的,子类都有,不会出现非法访问问题。但是如果用子类指针指向父类的话,一旦访问子类特有的方法函数或者成员变量,就会出现非法(专家级理解👍)。
#include <iostream> using namespace std; class A{ public: A(){ a = 1; b = 2; } private: int a; int b; }; class B{ public: B(){ c = 3; } void print(){ cout << c; } private: int c; }; int main() { A a; B* b = (B*)(&a); b->print(); return 0; }
输出:1(调换A中 a = 2;b = 1,输出为2)
将一个B类型的指针指向一个A类型的对象,A中无print函数,print是非虚函数,执行静态绑定,即在编译过程中绑定B类型的print函数。
当调用print函数时,需要输出c的值,程序并不知道指针b指向的并不是B类型的对象,
只是按照偏移值去取,c在B的对象中的偏移值跟a在A的对象中的偏移值相等(都位于对象的起始地址处),故取到a的值1
#include <iostream> using namespace std; class Shape { protected: int width, height; public: Shape( int a=0, int b=0) { width = a; height = b; } int area() { cout << "Parent class area :" <<endl; return 0; } }; class Rectangle: public Shape{ public: Rectangle( int a=0, int b=0):Shape(a, b) { } int area () { cout << "Rectangle class area :" <<endl; return (width * height); } }; class Triangle: public Shape{ public: Triangle( int a=0, int b=0):Shape(a, b) { } int area () { cout << "Triangle class area :" <<endl; return (width * height / 2); } }; // 程序的主函数 int main( ) { Shape *shape; Rectangle rec(10,7); Triangle tri(10,5); // 存储矩形的地址 shape = &rec; // 调用矩形的求面积函数 area shape->area(); // 存储三角形的地址 shape = &tri; // 调用三角形的求面积函数 area shape->area(); return 0; }
输出:
Parent class area :
Parent class area :
导致错误输出的原因是,调用函数 area() 被编译器设置为基类中的版本,这就是所谓的静态多态,或静态链接 - 函数调用在程序执行前就准备好了。
有时候这也被称为早绑定,因为 area() 函数在程序编译期间就已经设置好了。
但现在,让我们对程序稍作修改,在 Shape 类中,area() 的声明前放置关键字 virtual,如下所示:
class Shape { protected: int width, height; public: Shape( int a=0, int b=0) { width = a; height = b; } virtual int area() { cout << "Parent class area :" <<endl; return 0; } };
输出:
Rectangle class area :
Triangle class area :
此时,编译器看的是指针的内容,而不是它的类型。
因此,由于 tri 和 rec 类的对象的地址存储在 *shape 中,所以会调用各自的 area() 函数。
每个子类都有一个函数 area() 的独立实现。这就是多态的一般使用方式。
可以有多个不同的类,都带有同一个名称但具有不同实现的函数,函数的参数甚至可以是相同的
#include <iostream> using namespace std; //基类People class People{ public: People(char *name, int age); void display(); protected: char *m_name; int m_age; }; People::People(char *name, int age): m_name(name), m_age(age){} void People::display(){ cout<<m_name<<"今年"<<m_age<<"岁了,是个无业游民。"<<endl; } //派生类Teacher class Teacher: public People{ public: Teacher(char *name, int age, int salary); void display(); private: int m_salary; }; Teacher::Teacher(char *name, int age, int salary): People(name, age), m_salary(salary){} void Teacher::display(){ cout<<m_name<<"今年"<<m_age<<"岁了,是一名教师,每月有"<<m_salary<<"元的收入。"<<endl; } int main(){ People *p = new People("王志刚", 23); p -> display(); p = new Teacher("赵宏佳", 45, 8200); p -> display(); return 0; }
输出:
王志刚今年23岁了,是个无业游民。
赵宏佳今年45岁了,是个无业游民。
当基类指针 p 指向派生类 Teacher 的对象时,虽然使用了 Teacher 的成员变量,但是却没有使用它的成员函数
在编译语句People *p = new People(“王志刚”, 23);
没有声明虚函数,在编译期间就确定了p指针的类型是People类类型,那么当p指针使用display函数时,就使用People类的display函数。
语句p = new Teacher(“赵宏佳”, 45, 8200); 使得p指针指向的对象是Teacher对象,
指针就是一个地址而已,指向谁当然就使用谁里面的变量,理所当然就使用Teacher里面的成员变量,所以输出为:
执行的print函数是: p的类型中的函数(People的函数)
使用的变量是: p指向的对象的变量(Teacher的变量);
类中变量和函数是分开存储的,类的实例仅有变量,因此静态链接是链接函数,动态指向对象的变量(个人方便理解,叠buff)
因此动态链接了实例的变量,静态链接了函数。
使用纯虚函数 virtual 可以解决这个问题!!即将基类的函数前加 virtual,变为动态链接
也可也将其写为纯虚函数 virtual 函数() = 0;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)