INTERESTING AND OBSCURE INHERITANCE ISSUES WITH CPP
1. using 关键字
使用 using 关键字,可以将父类中被隐藏的函数暴露在子类中,但是需要注意的是,在相同情况下,子类函数的优先级更高。
2. 继承构造函数(C++11)
在c++11之前,构造函数、析构函数、赋值操作符,这些都不能被继承。但是,C++11允许我们使用 using 关键字来继承基类的构造函数。
示例1
示例2
3. 重写方法时的特殊情况
1) static 超类方法
在C++中,无法重写静态方法,因为方法不可能既是静态的又是虚的。如果子类中存在的静态方法与超类中的静态方法同名,实际上这是两个独立的方法。
2) 重写 private 或者 protected 超类方法
完全没有问题。记住:子类无法调用父类的 private 方法并不意味着无法重写这个方法。
4. 超类方法具有默认参数
子类与超类可以具有不同的默认参数,但使用的参数取决于声明的变量类型,而不是底层的对象。也就是说,C++根据描述对象的表达式类型绑定默认参数,而不是根据实际的对象类型参数。来看下面例子,使用的即为超类方法的默认参数。
再来看一个例子:
5. 子类方法具有不同的访问级别
我们可以修改父类方法在子类中的访问级别——可以加强限制也可以放宽限制。在 C++ 中,无论是加强限制还是放宽限制,意义都不是很大,但是这么做也是有合理原因的。
为了加强某个方法(或者数据成员)的限制,有两种方法。方法之一是修改整个子类的访问说明符,本文后面将讲述该方法。另一种方法是在子类中重新定义访问限制。
我们没有很好的方法(也没有很好的理由)来限制访问父类的 public 方法。
实际上,在子类中放宽访问限制是比较容易,也相对更有意义的。最简答的方法是在子类中提供一个 public 方法来访问父类的 protected 方法,如下所示:
调用 Blabber 对象的 public tell() 方法的用户代码可以有效地访问 Secret 类的 protected() 方法。当然,这并没有真正改变 dontTell() 的访问级别,只是提供了访问这个方法的 public 方法,对外提供了一个接口而已。
实际上,我们可以在 Blabber 子类中显式地重写 dontTell(),并将这个方法设置为 public 访问。来看以下代码:
以上代码如果通过 blabber 来调用 dontTell() 当然没有问题,但是由于父类中的 protected 方法仍然是 protected 的,因此使用指针或者引用来调用 Secret 的 dontTell() 将无法通过编译。
注意:在工程中唯一有益的,是对父类中的 protected 方法提供较为宽松的访问限制,也就是放宽限制。
6. 子类中的复制构造函数以及赋值运算符
当我们需要在类中动态分配内存时,提供一个 copy constructor 和 assignment operator 是必要的。当定义子类时,我们需要特别关注 copy constructors 以及 operatpr=。
无论父类是否定义了非默认的拷贝构造函数以及复制赋值运算符,只要子类没有特殊的数据成员(通常是指针)需要我们定义一个非默认的拷贝构造函数或者复制赋值运算符,那么我们就无需定义。如果子类省略了 copy constructor 或者 operator=,那么编译器会为子类中的数据成员提供默认的 copy constructor 以及 operator=,同时父类中的 copy constructor 和 operator= 会被用于父类中的数据成员。
另一方面,如果在子类中自定义了 copy constructor,我们就需要在该 copy constructor 中显式地调用父类的 copy constructor。 如果不这么做,那么默认构造函数(不是拷贝构造函数)将被用于对象中的父类成分。
[Why?我个人是这样理解的,因为通常一个类中数据成员都是私有的,那么只有父类的构造函数(无论是默认构造函数还是拷贝构造函数)才能对这些 private 数据成员进行初始化,为了统一,cpp便规定子类中的构造函数必须调用父类的构造函数来对父类部分的数据成员进行初始化。]
相似的,如果子类需要重写 operator=,调用父类版本的 operator= 往往是必要的([事实上,对于非 private 数据成员即使不调用父类的 operator= 来完成赋值,语法上也没有错,这点不像拷贝构造函数那样严格])。如果不这样做,可能处于某些很奇怪的原因,使得你只想对 object 的部分数据成员进行赋值。下面的代码示范了如何在子类中调用父类的operator=。
If your subclass does not specify its own copy constructor or operator=, the
parent functionality continues to work. If the subclass does provide its own copy
constructor or operator=, it needs to explicitly reference the parent versions.
如果想禁止复制一个类,应该怎么办?显然我们可以把类的复制构造函数设为private,但是这样一来该类的 friend 成员仍然可以复制该类,于是我们只声明这个函数,而不去实现。另外,如果你不需要复制该类的对象,最好把赋值运算也一并禁用掉。
所以这里的做法是:把复制构造函数和赋值运算符的声明设为 private 而不去实现。
实际上,更通用的做法是写一个类noncopyable,凡是继承该类的任何类都无法复制和赋值。Why?子类想要定义拷贝构造函数以及赋值运算符,却发现没有办法调用父类的拷贝构造函数以及赋值运算符。
7. The Need for virtual Destructors
无论如何,我们一定要将析构函数设置为 virtual,因为不这么做,很容易导致内存泄漏。来看以下例子:
Unless you have a specific reason not to, we highly recommend making all
methods, including destructors but not constructors, virtual. Constructors
cannot and need not be virtual because you always specify the exact class
being constructed when creating an object.
当然,将赋值运算符设置为 virtual 意义并不大,因为父类引用或者指针只能管到自己本身的数据成员。
INTERESTING AND OBSCURE INHERITANCE ISSUES WITH CPP的更多相关文章
- mac 下的 homebrew
如果安装了macport 就不能安装homebrew ,必须先卸载macport $ sudo port -f uninstall installed$ sudo rm -rf \/opt/local ...
- DependencyProperties or INotifyPropertyChanged ?
When you want to make an object binding-aware you have two choices : implements INotifyPropertyChang ...
- PHP filesystem attack vectors
http://www.ush.it/2009/02/08/php-filesystem-attack-vectors/ On Apr 07, 2008 I spoke with Kuza55 and ...
- Swift中面向协议的编程
什么是面向协议的编程? 面向协议的编程,是一种编程范式. 编程范式,是一个计算机科学用语.维基百科中的解释是,计算机编程的基本风格或典型模式.通俗来说,就是解决某一个问题的方法不同方法和思路. 像大家 ...
- 为什么swift是面向协议的编程--对面向对象机制的改进
主要目标是提供抽象能力和解决值类型的多态问题 Actually, Abrahams says, those are all attributes of types, and classes are j ...
- Spring Security(九):2.4.4 Checking out the Source(检查来源)
Since Spring Security is an Open Source project, we’d strongly encourage you to check out the source ...
- ASP.NET 4.0 forms authentication issues with IE11
As I mentioned earlier, solutions that rely on User-Agent sniffing may break, when a new browser or ...
- CF #365 (Div. 2) D - Mishka and Interesting sum 离线树状数组
题目链接:CF #365 (Div. 2) D - Mishka and Interesting sum 题意:给出n个数和m个询问,(1 ≤ n, m ≤ 1 000 000) ,问在每个区间里所有 ...
- Think Python - Chapter 18 - Inheritance
In this chapter I present classes to represent playing cards, decks of cards, and poker hands.If you ...
随机推荐
- Erlang generic standard behaviours -- gen
在分析 gen_server (或者是gen_fsm )之前,首先应该弄明白,gen 这个module . -module(gen). -compile({inline,[get_node/1]}). ...
- Hadoop之Hive自定义函数的陷阱
A left join B, 这个B会连到A. 如<A1,B>, <A2,B>,在处理第一条记录的时候将B.clear(),则第二条记录的B是[]空的这是自定义UDF函数必须注 ...
- GNU make 总结 (三)
一.makefile 变量 makefile中的变量名是大小写敏感的,例如”foo”和”Foo”是两个不同的变量.通常情况下,对于一般变量,我们可以使用小写形式,而对于参数变量,采用全大写形式.当我们 ...
- Hibernate从入门到精通(十一)多对多双向关联映射
上次我们在中Hibernate从入门到精通(十)多对多单向关联映射讲解了一下多对多单向关联映射,这次我们讲解一下七种映射中的最后一种多对多双向关联映射. 多对多双向关联映射 按照我们之前的惯例,先看一 ...
- iOS UIView的简单渐变效果
这里主要用到了ios4.0以后 UIView的类方法 animateWithDuration: 函数原型包括以下类方法 + (void)animateWithDuration:(NSTimeInter ...
- xml基础学习笔记05
Xpath快速解析 如题一样,本篇主要说说Xpath快速查找XML文档 * Xpatn.Xquery,是专门用来查询xml的语言 * 查询xml非常快 Xpatn.Xquery,是专门用来 ...
- MyEclipse运行很慢的原因
myEclipse以其丰富的功能博得程序员的热爱,但是其速度确实有问题,jsp文 件打开会不停的校验,甚至出现卡死,分析原因,原来是 validation在做怪. 好,既然找到了原因,那就把问题解决, ...
- 【BZOJ】【2500】幸福的道路
树形DP+单调队列优化DP 好题(也是神题……玛雅我实在是太弱了TAT,真是一个250) 完全是抄的zyf的……orz我还是退OI保平安吧 第一步对于每一天求出一个从第 i 个点出发走出去的最长链的长 ...
- 【BZOJ】【4011】【HNOI2015】落忆枫音
拓扑排序+DP 题解:http://blog.csdn.net/PoPoQQQ/article/details/45194103 http://www.cnblogs.com/mmlz/p/44487 ...
- Matlab动态数组实现
clear all; clc; a = []; %不是null,也不能什么都不是 for i=1:10 a = [a i]; end