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 ...
随机推荐
- 九度oj 1521 二叉树的镜像
原题链接:http://ac.jobdu.com/problem.php?pid=1521 水题,如下.. #include<algorithm> #include<iostream ...
- spring使用JdbcDaoSupport中封装的JdbcTemplate进行query
1.Dept package cn.hxex.springcore.jdbc; public class Dept { private Integer deptNo; private String d ...
- 条款5:了解C++提供的默认函数
当我们定义一个类时,如果没有声明任何函数,那么C++编译器会默认提供4个函数:默认构造函数.复制构造函数.赋值操作符函数.析构函数,并且这些函数默认都是public且inline的.因此,当你定义如下 ...
- IIS 404.17 错误解决方案
操作方法:在管理员身份打开命令行,运行以下命令: C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis -i
- Android Paint的使用以及方法介绍(附源码下载)
要绘图,首先得调整画笔,待画笔调整好之后,再将图像绘制到画布上,这样才可以显示在手机屏幕上.Android 中的画笔是 Paint类,Paint 中包含了很多方法对其属性进行设置,主要方法如下: se ...
- BZOJ1500 维修数列
AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=1500 [前言] 据说没打这题就相当于没打过Splay,这题简直就是让你内心崩溃的... 这 ...
- 【BZOJ】【TJOI2015】线性代数
网络流/最小割/最大权闭合图 2333好开心,除了一开始把$500^2$算成25000……导致数组没开够RE了一发,可以算是一次AC~ 咳咳还是回归正题来说题解吧: 一拿到这道题,我就想:这是什么鬼玩 ...
- Effective Java总结
规则1. 用静态工厂方法代替构造器 例子: public class Example { } public class StaticFactory { //valueOf/Of/getInstance ...
- orbis 链接 .a的问题
orbis-clang.exe :error: no such file or directory : libppfxd_delta.a 这个东西真是见鬼 明明在那里就是说找不到 在依赖里libppf ...
- VS开发工具 不会在异常的地方停止的问题.
启用"仅我的代码"