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;

posted on   廿陆  阅读(8)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示