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 ...
随机推荐
- hdu 1874 畅通工程续
题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=1874 畅通工程续 Description 某省自从实行了很多年的畅通工程计划后,终于修建了很多路.不过 ...
- metaq
MetaQ(全称Metamorphosis)是一个高性能.高可用.可扩展的分布式消息中间件,思路起源于LinkedIn的Kafka,但并不是Kafka的一个Copy.MetaQ具有消息存储顺序写.吞吐 ...
- VS2012 常用web.config配置解析之自定义配置节点
在web.config文件中拥有一个用户自定义配置节点configSections,这个节点可以方便用户在web.config中随意的添加配置节点,让程序更加灵活(主要用于第三方插件的配置使用) 自定 ...
- Centos 安装 p7zip,即Linux下的7z
Centos 无法直接通过yum安装7z,我们一般通过repoforge,rpmforge的软件包进行安装,你只需要下载一个对应的包,直接安装就可以 p7zip-9.20.1-1.el4.rf.i38 ...
- 000 VS2013 c++ 框架
#include <Windows.h> //全局函数声明 LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, ...
- ALTERA MAX10官方评估板,新鲜出炉!
刚刚拿到骏龙提供的ALTERA MAX10官方评估板,还热乎呢,呵呵!赶紧跟大家分享一下 板子很简单,把IO口都扩展出来了,其他功能基本上没有. FPGA型号是10M08SAE144C8GES,144 ...
- Qt 按键长按的处理
keyPressEvent()部分代码: if (e->key() == Qt::Key_A && e->isAutoRepeat()) { if (!mPressFl ...
- 内部类&匿名内部类
内部类:如果A类需要直接访问B类中的成员,而B类又需要建立A类的对象.这时,为了方便设计和访问,直接将A类定义在B类中.就可以了.A类就称为内部类.内部类可以直接访问外部类中的成员.而外部类想要访问内 ...
- Lua与C++交互初探之Lua调用C++
Lua与C++交互初探之Lua调用C++ 上一篇我们已经成功将Lua的运行环境搭建了起来,也成功在C++里调用了Lua函数.今天我来讲解一下如何在Lua里调用C++函数. Lua作为一个轻量级脚本语言 ...
- TList,TObjectList 使用——资源释放
TOjectList = Class (Tlist); TOjectList继承Tlist,从名字上看就可以知道它是专门为对象列表制作的,那么他到底丰富了那些功能呢? 首先是 TObject 作为对象 ...