C++学习之继承篇(is a:用子类对象给基类对象赋值或初始化)

1.下面的例子代码,就说明了标题:



2.从内存的角度介绍is a:

如下图所示:当基类含有两个数据成员m_strName和m_iAge时,不管是公有私有还是保护类型的,都会被子类继承过来,同时子类应该还有他自身的数据成员,m_strCode和m_ISalary,当我们用子类的对象给基类的对象赋值或者初始化基类的对象时,它的本质就是将从父类继承来的数据成员的值赋给父类的对象,而此时子类对象中的其他数据将会被截断,也就是丢失。


如果用父类指针指向子类对象,那么父类指针也只能访问到父类所拥有的数据成员和成员函数,而无法访问到子类所独有的数据成员和成员函数。



基于上述内存结构,有一个问题!!!如下图所示:


当用基类指针指向子类从堆中分配的对象时,如下形式 A*p = new B;当调用delete p;p=NULL;销毁对象时,是调用父类A的析构函数还是调用子类B的构造函数呢?如下图所示:答案是会调用父类的构造函数,这样问题就来了,子类不是从父类继承来的那些独有的成员变量的内存将得不到释放,将会造成内存泄露,这种情况应该如何避免内存泄露

呢?这就引入了一个新的知识点:虚析构函数。

虚析构函数就是在父类的析构函数前加上virtual关键字,这种特性将会被继承下去,也即子类的析构函数也为虚析构函数,在下面的例子中做如下改变:

virtual ~Person(){}//将父类的析构函数变为虚析构函数

virtual ~Soldier(){}//子类的析构函数继承了这种特性,也变成了虚析构函数,即便子类不写virtual,子类构造函数也是虚析构函数

虚析构函数的使用场合:当存在继承关系,用父类的指针指向从堆中分配的子类的对象时,然后又想用父类的指针去释放掉内存,就会用到虚析构函数,用了虚析构函数后,再调用delete Person时,就会先调用子类的析构函数,再调用父类的构造函数了,如下图所示:



3.从函数参数的角度分析is a 的关系

第一个函数,参数是对象父类对象,我们的实参可以是父类的对象,也可以是子类的对象,打印出的结果显示,实例化子类时,会先调用父类的构造函数,再调用自己的构造函数。

因为参数是父类Person的对象我们再调用test1给它传参数的时候,就会临时的产生一个对象p,用它来调用play函数,并且在函数执行完毕以后,p这个临时对象就会被销毁,因为调用了两次test1,这就是为什么打印出的结果中会调用两次父类Person的析构函数。但是注意的是实参会给临时对象p赋值,即发生了对象之间的赋值,所以会调用Person的拷贝构造函数如下图所示:



如果同样的参数调用test2(Person &p),调用代码如下:test2(p);test2(s);打印出的结果如下图所示,可见并没有调用析构函数,这是因为用引用作为餐宿的时候,实参传进来的时候,只是相当于给实参起了一个别名p,并没有产生临时的对象,所以函数执行完的时候也不用析构临时对象,就不会调用~Person()了;

如果调用test3(Person* p),调用代码如下:test3(&p);test3(&s);打印出的结果和调用test2(Person& p)一样,也没有调用析构函数,原因是:用指针作为参数时,只是让指针去指向实参的地址,所以也没有产生临时对象,所以函数执行完后也不会调用析构函数。

因为test2()和test3()用引用和指针这两种形参形式都不会产生临时变量,不用去调用拷贝构造函数和析构函数,所以执行效率更高。


补充小知识点:你区分的了吗?

前提:class B:public A

B b;

A a = b;//用子类B的对象初始化父类A的对象

A a1;

a1 = b;//用子类B的对象赋值给父类A的对象

A*p = &b;//用父类A的指针指向子类B的对象b

A&a2 = b;//用子类B的对象初始化父类A的引用

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值