博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++继承和多态实用经验
阅读量:4100 次
发布时间:2019-05-25

本文共 3742 字,大约阅读时间需要 12 分钟。

文章内容均整理自    刘光《C++程序员不可不知的101条实用经验》
1.多态基类的析构函数应为虚函数
小心陷阱:
(1)在C++中,当一个派生类对象通过使用一个积累指针删除,而这个基类有一个非虚的析构函数,则结果是未定义的。运行时比较有代表性的后果是对象的派生部分不会被销毁。然而,基类部分很可能已被销毁,这就导致一个奇怪的“部分析构”对象,这是一个泄漏资源。
(2)排除这个问题的方法是,
给基类一个虚析构函数。于是删除一个派生类对象时就有了所期望的正确行为:销毁整个对象,包括全部的派生类部分
请谨记:
(1)
派生类的析构函数一般不为虚函数,因为虚函数的实现要求对象携带额外信息,这些信息用于在运行时确定该对象应该调用哪一个虚函数
(2)如果将基类的析构函数定义为纯虚函数,纯虚析构函数的虚拟实现,即
空实现能够保证代码的安全性。
(3)
不能随便定义虚析构函数,若基类中其他成员函数都不是虚函数时,定义析构函数为虚函数,会引入虚函数表,造成运行时的性能损失
(4)如果不想让外面的用户直接构造一个A类的对象,而希望用户只能构造A类的子类,就可以将A类的构造/析构函数声明为protected,而将A类的子类的 构造/析构函数声明为public的。
2.明晰public,protected,private 3中继承差别
单继承和多继承区别:
(1)多继承与单继承的区别从定义格式上看,主要是多继承的基类多于一个,而单继承的基类只有一个
(2)单继承永远不会存在对象切片问题,而多继承如果使用不知义,容易出现对象切片
(3)多继承相对于单继承,还会存在所有的基类继承一个公共父类的问题。这会涉及虚继承的问题。
①public继承可见性
(1)基类成员对其对象的可见性:公有成员可见,其他不可见,这里保护成员同于私有成员
(2)基类成员对派生类的可见性:公有成员和保护成员可见,而私有成员不可见。
(3)基类成员对派生类对象的可见性:公有成员可见。其他成员不可见。
②private继承可见性(
基类的成员只能由直接派生类访问,无法再往下继承
(1)基类成员对其对象的可见性:公有成员可见,其他成员不可见。
(2)基类成员对派生类的可见性:公有成员和保护成员可见,而私有成员不可见。
(3)基类成员对派生类对象的可见性:所有成员都不可见
③protected继承可见性
(1)基类成员对其对象的可见性:公有成员可见,其他成员不可见。
(2)基类成员对派生类的可见性:公有成员和保护成员可见,而私有成员不可见。
(3)基类成员对派生类对象的可见性:所有成员都不可见
继承关系访问规则:
称派生类的对象对基类访问为水平访问,派生类的派生类对基类的访问为垂直访问。
(1)公有继承时,水平访问和垂直访问对基类中的公有成员不受限制。
(2)私有继承时,水平访问和垂直访问对基类中的公有成员也不能访问。
(3)保护继承时,对于垂直访问同于公有继承,对于水平访问同于私有继承。
(4)基类中的私有成员,只能被基类中的成员函数和友元函数所访问,不能被其他的函数访问。
3.慎用多继承机制
除非有必要,否则禁止使用多继承。
4.时刻提防对象切片
对象切片:如果一个派生类对象向上强制转换为基类,用的是传值的方式,而不是指针和引用 ,那么这个派生类对象在向上转换后,将会被切分成基类对象,派生类中独有的成员变量和方法都被切分掉了,只剩下和基类相同的成员变量和属性。这个派生类对象被切成了一个基类对象。
请谨记:
对象且前和拷贝构造是一个很复杂的东西,在有虚拟机制的情况下两者是紧密结合在一起的。因为对象切片和拷贝构造函数的问题,不通过指针或者引用无法达到多态的目的。
5.明晰派生类构造/析构运行原理
单继承:进行对象构造时从基类开始到对象类,
析构时是构造时的逆序
多继承:进行对象构造时从基类开始(其中基类的构造顺序按照类声明的顺序),析构时是构造时的逆序。
6.谨慎使用private继承
private继承原理:
(1)编译器不会吧派生类转型为基类
(2)基类汇总的所有public、protected成员函数会成为派生类中的private成员
(3)派生类和基类不是is a关系,而是一种实现关系。
请谨记:
(1)私有继承意味着是根据...实现的。他通常比复合更低级,但当一个派生类需要访问保护基类成员或需要重定义继承来的虚拟函数时它就是合理的
(2)与复合不同,私有继承能使空基类优化有效。
7.区分overloading、overriding、hiding的差异
重载的条件:
(1)至少有两个以上的函数具有相同的标识符名称。
(2)函数的形参个数或类型不同或是形参的顺序不同
(3)函数的返回值相同与否,不能用于判断两个函数是否互为重载函数
(4)互为重载的函数必须具备相同的作用域。不同作用域内的同名函数通过作用域来区分不形成重载。
重写的条件:
(1)
被重写的函数不能是static,必须是虚函数的,即使直接基类未声明为虚函数,最原始的基类中也必须声明为虚函数
(2)重写必有具有相同的函数原型
(3)重写函数的访问修饰符可以不同
(4)
const可能会使虚函数成员重写失败
隐藏的条件:
派生类中的函数屏蔽了基类中的同名函数的非虚函数,子类重新定义父类中有相同名称的非虚函数(参数列表可以不同),重定义后子类调用的函数是子类自己的函数,若想调用父类的该同名函数,需要加上父类作用域来制定调用的函数
隐藏涉及作用域问题,因为编译在实现函数调用时,首先会查找本类中的函数,只要本类中有函数名称满足要求即停止查找。如果查找不到再沿着继承链逐级向上查找,知道查找到函数名称为止。由于派生链中派生类属于下级,存在同名函数时,编译最终选择派生类中的函数,这样一来基类中的同名函数就会被屏蔽了
8.确保public继承是“is-a”关系
请谨记:
(1)在C++公有继承中,
派生类和基类应体现一种is-a关系,即基类的所有实现,派生类都是满足的
(2)在公有继承设计中,我们需要考虑常识,同时也要兼顾is-a的客观概念,只有这样,设计出的继承和多态才能满足实际编程的需要。
9.区分接口继承和实现继承
(1)接口继承,派生类是基类定义的延续。这种方式先定义一个抽象基类,该基类中有些操作并未实现:然后定义非抽象对象的派生类,实现抽象基类中定义的操作。虚函数就属于此类情况。这时,派生类是抽象的基类的实现,即可看成是基类定义的延续,这也是派生类的一种常用方法。
(2)实现继承,派生类是基类的具体化,类的层次通常反映了客观世界中某种真实的模型。在这种情况下,不难看出:基类是对若干个派生类的抽象,而派生类是基类的具体化。基类抽取了它的派生类的公共特征,而派生类通过增加行为将抽象类变为某种有用的类型。
请谨记:
(1)接口继承和实现继承是不同的。在public继承下派生类总是继承基类的接口
(2)纯虚函数只具体指定接口继承,而不关注接口实现
(3)非纯虚函数具体指定接口继承和接口实现,非虚函数具体指定接口继承和强制性实现
10.不要重新定义继承而来的非虚函数
任何情况下都要禁止重新定义继承而来的非虚函数,以防产生函数覆盖现象
11.绝对不要重新定义继承而来的默认参数
带默认参数的虚函数实现时,虚函数采用动态绑定策略,而默认参数采用的却是静态绑定策略。在基类虚函数中声明的默认函数,即使派生类中重新指定了默认参数,在多态调用时,依然采用基类虚函数中的默认参数。
请谨记:
绝对不要重新定义继承而来的默认参数值,因为默认参数值都是静态绑定的,而虚函数是唯一应该覆写的东西,即动态绑定。
12.切忌继承多度滥用
桥接模式:将抽象部分和实现部分进行分离,使其分别可以独立变化。桥接模式中抽象和实现分离指抽象类和他的派生类用来实现自己的对象。核心意图是把实现独立出来,让他们各自变化。这使得每种实现的变化不会影响其他实现,从而达到应对变化的目的。
请谨记:
过广或过深的继承往往不是良好设计的表现。这种继承的出现主要是类型职责划分不清楚造成的。对继承滥用保持警惕,同时警惕常理逻辑在软件设计中的负面影响。
13.虚函数重载的陷阱
虚函数说明:
(1)虚函数必须是一个成员函数,而且是一个非static的成员函数
(2)与基类的虚函数有相同的参数个数,其参数类型与基类的虚函数的对应参数类型相同
(3)其返回值或与基类虚函数的相同,或都返回指针或引用,并且派生类虚函数所返回的指针或引用的基类型是基类中被替换的虚函数所返回的指针或引用基类型的子类型
(4)无论是基类中的虚函数,还是派生类中的虚函数,两者应该是完全相同的声明。函数标签也需要一样。
请谨记:
不要试图重载虚函数,否则会为此付出代价。
14.关于虚赋值的问题
虚赋值:将重载赋值运算符实现声明为虚函数,称为虚赋值,赋值运算符声明为虚函数是合法的,但是虚赋值却不合理。
原型模式:基类提供一个纯虚函数,而后派生类覆写,返回一个派生类对象的引用或指针。
请谨记:
应该尽量避免使用虚赋值,通过原型模式替代实现对象的复制拷贝

转载地址:http://awzsi.baihongyu.com/

你可能感兴趣的文章
Redis | Redis 单机版安装与使用(Window)
查看>>
IDEA 搭建 maven 环境
查看>>
SpringMVC(四):接收参数的方式
查看>>
IDEA导入Eclipse项目静态资源无法加载等问题
查看>>
区块链入门一
查看>>
区块链入门二
查看>>
java爬虫WebMagic框架爬取图片
查看>>
一个简单漂亮的前端聊天界面
查看>>
使用PowerDesigner绘制数据流图
查看>>
使用PowerDesigner绘制ER图和生成Sql Server表
查看>>
成组链接法详解+Java代码
查看>>
数据库系统思维导图
查看>>
SpringMVC(五):EditorMD编辑器开发与文件上传
查看>>
Java 基础 | Int 和 Integer
查看>>
Android 使用 Socket 连接阿里云服务器
查看>>
JSP与Servlet(四):java实现文件上传下载
查看>>
安全框架shiro(二):shiro进阶
查看>>
Linux(三):Linux文件互传。
查看>>
Linux(二):阿里云CentOS7部署SpringBoot项目
查看>>
JPA(一):十分钟入门 JPA
查看>>