1.  仅仅能初始化直接基类

一个类仅仅能初始化自己的直接基类。直接就是在派生列表中指定的类。假设类C 从类B 派生,类B 从类A 派生,则B 是C 的直接基类。尽管每一个C 类对象包括一个A 类部分,但C 的构造函数不能直接初始化A 部分。相反,须要类C 初始化类B,而类B 的构造函数再初始化类A。这一限制的原因是,类B 的作者已经指定了如何构造和初始化B 类型的对象。像类B 的不论什么用户一样,类C 的作者无权改变这个规约。

2. 重构

将Disc_item 加到Item_base 层次是重构(refactoring)的一个样例。重构包含又一次定义类层次,将操作和/或数据从一个类移到还有一个类。为了适应应用程序的须要而又一次设计类以便添加新函数或处理其它改变时,最有可能须要进行重构。

重构常见在面向对象应用程序中很常见。值得注意的是,尽管改变了继承层次,使用Bulk_item 类或Item_base 类的代码不须要改变。然而,对类进行重构,或以随意其它方式改变类,使用这些类的随意代码都必须又一次编译。

3. 尊重基类接口

构造函数仅仅能初始化其直接基类的原因是每一个类都定义了自己的接口。定义Disc_item 时,通过定义它的构造函数指定了如何初始化Disc_item 对象。一旦类定义了自己的接口,与该类对象的全部交互都应该通过该接口,即使对象是派生类对象的一部分也不例外。相同,派生类构造函数不能初始化基类的成员且不应该对基类成员赋值。假设那些成员为public 或protected,派生构造函数能够在构造函数函数体中给基类成员赋值,可是,这样做会违反基类的接口。派生类应通过使用基类构造函数尊重基类的初始化意图,而不是在派生类构造函

 class Derived: public Base {
public:
// Base::~Base invoked automatically

数函数体中对这些成员赋值。

4. 派生类析构函数

析构函数的工作与复制构造函数和赋值操作符不同:派生类析构函数不负责撤销基类对象的成员。编译器总是显式调用派生类对象基类部分的析构函数。每一个析构函数仅仅负责清除自己的成员:

5. 虚析构函数

假设删除基类指针,则须要执行基类析构函数并清除基类的成员,假设对象实际是派生类型的,则未定义该行为。要保证执行适当的析构函数,基类中的析构函数必须为虚函数:

 class Item_base {
public:
// no work, butvirtual destructor needed
// if base pointer thatpoints to a derived object is ever deleted
virtual ~Item_base(){ }
};

假设析构函数为虚函数,那么通过指针调用时,执行哪个析构函数将因指针所指对象类型的不同而不同:

 Item_base *itemP =new Item_base; // same static and dynamic type
delete itemP; // ok:destructor for Item_base called
itemP = newBulk_item; // ok: static and dynamic types differ
delete itemP;<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>

6. 作用域与成员函数

在基类和派生类中使用同一名字的成员函数,其行为与数据成员一样:在派生类作用域中派生类成员将屏蔽基类成员。即使函数原型不同,基类成员也会被屏蔽:

 struct Base {
int memfcn();
};
struct Derived : Base{
int memfcn(int); // hides memfcn in the base
};
Derived d; Base b;
b.memfcn(); // calls Base::memfcn
d.memfcn(10); // calls Derived::memfcn
d.memfcn(); // error: memfcn with no arguments is hidden
d.Base::memfcn(); // ok: calls Base::memfcn



7.  虚函数与作用域

还记得吗,要获得动态绑定,必须通过基类的引用或指针调用虚成员。当我们这样做时,编译器将在基类中查找函数。假定找到了名字,编译器就检查实參是否与形參匹配。

如今能够理解虚函数为什么必须在基类和派生类中拥有同一原型了。假设基类成员与派生类成员接受的实參不同,就没有办法通过基类类型的引用或指针调用派生类函数。考虑例如以下(人为的)为集合:

class Base {
public:
virtual int fcn();
};
class D1 : public Base{
public:
// hides fcn in thebase; this fcn is not virtual
int fcn(int); // parameter list differs from fcn in Base
// D1 inherits definition of Base::fcn()
};
class D2 : public D1{
public:
int fcn(int); // non virtual function hides D1::fcn(int)
int fcn(); // redefines virtual fcn from Base
};

D1 中的fcn 版本号没有重定义Base 的虚函数fcn,相反,它屏蔽了基类的fcn。结果D1 有两个名为fcn 的函数:类从Base 继承了一个名为fcn 的虚函数,类又定义了自己的名为fcn 的非虚成员函数,该函数接受一个int 形參。可是,从Base 继承的虚函数不能通过D1 对象(或D1 的引用或指针)调用,由于该函数被fcn(int) 的定义屏蔽了。

类D2 重定义了它继承的两个函数,它重定义了Base 中定义的fcn 的原始版本号并重定义了D1 中定义的非虚版本号

8. 通过基类调用被屏蔽的虚函数

通过基类类型的引用或指针调用函数时,编译器将在基类中查找该函数而忽略派生类:

 Base bobj; D1 d1obj;D2 d2obj;
Base *bp1 =&bobj, *bp2 = &d1obj, *bp3 = &d2obj;
bp1->fcn(); // ok:virtual call, will call Base::fcn at run time
bp2->fcn(); // ok:virtual call, will call Base::fcn at run time
bp3->fcn(); // ok:virtual call, will call D2::fcn at run time

9. 名字查找与继承

理解C++ 中继承层次的关键在于理解怎样确定函数调用。确定函数调用遵循下面四个步骤:

1. 首先确定进行函数调用的对象、引用或指针的静态类型。

2. 在该类中查找函数,假设找不到,就在直接基类中查找,如此循着类的继承链往上找,直到找到该函数或者查找完最后一个类。假设不能在类或其相关基类中找到该名字,则调用是错误的。

3. 一旦找到了该名字,就进行常规类型检查查看假设给定找到的定义,该函数调用是否合法。

4. 假定函数调用合法,编译器就生成代码。假设函数是虚函数且通过引用或指针调用,则编译器生成代码以确定依据对象的动态类型执行哪个函数版本号,否则,编译器生成代码直接调用函数。

将派生类对象拷贝到基类对象时,派生类对象将被切掉

10. 句柄类与继承

C++ 中面向对象编程的一个颇具讽刺意味的地方是,不能使用对象支持面向对象编程,相反,必须使用指针或引用。比如,以下的代码段中:

 void get_prices(Item_base object,
const Item_base *pointer,
const Item_base &reference)
{
// which version ofnet_price is called is determined at run time
cout <<pointer->net_price(1) << endl;
cout <<reference.net_price(1) << endl; // always invokesItem_base::net_price
cout <<object.net_price(1) << endl;
}

通过pointer 和reference 进行的调用在执行时依据它们所绑定对象的动态类型而确定。

C++学习笔记11-面向对象2的更多相关文章

  1. Spark学习笔记11面向对象编程

    面向对象编程   11.1 object类 11.1.1定义一个简单的类   11.1.2 field的getter与setter 定义类包含,定义类的field及方法.其格式如下 class Cla ...

  2. ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET MVC 学习笔记-6.异步控制器 ASP.NET MVC 学习笔记-5.Controller与View的数据传递 ASP.NET MVC 学习笔记-4.ASP.NET MVC中Ajax的应用 ASP.NET MVC 学习笔记-3.面向对象设计原则

    ASP.NET MVC 学习笔记-7.自定义配置信息   ASP.NET程序中的web.config文件中,在appSettings这个配置节中能够保存一些配置,比如, 1 <appSettin ...

  3. Ext.Net学习笔记11:Ext.Net GridPanel的用法

    Ext.Net学习笔记11:Ext.Net GridPanel的用法 GridPanel是用来显示数据的表格,与ASP.NET中的GridView类似. GridPanel用法 直接看代码: < ...

  4. UML和模式应用学习笔记-1(面向对象分析和设计)

    UML和模式应用学习笔记-1(面向对象分析和设计) 而只是对情节的记录:此处的用例场景为:游戏者请求掷骰子.系统展示结果:如果骰子的总点数是7,则游戏者赢得游戏,否则为输 (2)定义领域模型:在领域模 ...

  5. SQL反模式学习笔记11 限定列的有效值

    目标:限定列的有效值,将一列的有效字段值约束在一个固定的集合中.类似于数据字典. 反模式:在列定义上指定可选值 1. 对某一列定义一个检查约束项,这个约束不允许往列中插入或者更新任何会导致约束失败的值 ...

  6. golang学习笔记11 golang要用jetbrain的golang这个IDE工具开发才好

    golang学习笔记11   golang要用jetbrain的golang这个IDE工具开发才好  jetbrain家的全套ide都很好用,一定要dark背景风格才装B   从File-->s ...

  7. Spring MVC 学习笔记11 —— 后端返回json格式数据

    Spring MVC 学习笔记11 -- 后端返回json格式数据 我们常常听说json数据,首先,什么是json数据,总结起来,有以下几点: 1. JSON的全称是"JavaScript ...

  8. Lua学习笔记:面向对象

    Lua学习笔记:面向对象 https://blog.csdn.net/liutianshx2012/article/details/41921077 Lua 中只存在表(Table)这么唯一一种数据结 ...

  9. Python3+Selenium3+webdriver学习笔记11(cookie处理)

    #!/usr/bin/env python# -*- coding:utf-8 -*-'''Selenium3+webdriver学习笔记11(cookie处理)'''from selenium im ...

  10. 并发编程学习笔记(11)----FutureTask的使用及实现

    1. Future的使用 Future模式解决的问题是.在实际的运用场景中,可能某一个任务执行起来非常耗时,如果我们线程一直等着该任务执行完成再去执行其他的代码,就会损耗很大的性能,而Future接口 ...

随机推荐

  1. 前端压缩图片,前端压缩图片后转换为base64.

    今天利用一上午研究了一下前端如何将5m左右的照片转换base64大小为 100k以内! 有两个链接:https://www.cnblogs.com/007sx/p/7583202.html :http ...

  2. 路飞学城Python-Day7(practise)

    # 1.编码问题# i.请说明python2与python3中的默认编码是什么?# python2中的默认编码是ASCII码,只能识别英文等其他字符# python3中的默认编码是utf-8# ii. ...

  3. Mysql学习总结(26)——MySQL子查询

    mysql中虽然有连接查询实现多表连接查询,但是连接查询的性能很差,因此便出现了子查询. 1.理论上,子查询可以出现在查询语句的任何位置,但实际应用中多出现在from后和where后.出现在from后 ...

  4. R语言学习(一)前言

    本系列文章由 @YhL_Leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/49768161 R是一个有着统计分析功能 ...

  5. 埃及分数 迭代加深搜索 IDA*

    迭代加深搜索 IDA* 首先枚举当前选择的分数个数上限maxd,进行迭代加深 之后进行估价,假设当前分数之和为a,目标分数为b,当前考虑分数为1/c,那么如果1/c×(maxd - d)< a ...

  6. ASP.NET-缓存outputcache参数

    给Index加一个60秒的缓存,应该缓存在IIS服务器里面(我猜的) 只对变化的参数page不进行缓存,其他参数返回相同的内容 根据接受的语言的不同不进行缓存 设定缓存的位置 依赖于数据库变化的缓存 ...

  7. lenovo G系列重装系统

    lenovo G41 的笔记本默认安装的是win8 中文版 的操作系统,使用非常不方便,用U盘重装成WIN7的系统. 1.用启动工具软件制作U盘启动盘.  详细能够參照  http://www.uqi ...

  8. 公布Qt Widgets桌面应用程序的方法

    公布Qt Widgets桌面应用程序的方法 Qt是一款优秀的跨平台开发框架,它能够在桌面.移动平台以及嵌入式平台上执行.眼下Qt 5介绍程序公布的文章帖子比較少.大家又很想要知道怎样公布Qt应用程序, ...

  9. 前端到后台ThinkPHP开发整站--php开发案例

    前端到后台ThinkPHP开发整站--php开发案例 总结 还是需要做几个案例,一天一个为佳,那样才能做得快. 从需求分析着手,任务体系要构建好,这样才能非常高效. 转自: 前端到后台ThinkPHP ...

  10. SQL Server单表已700w+将普通表转换成分区表1

    最近项目中,某个表数据量爆发时增长,单表已700w+,读写性能急剧下降,所以考虑加入分区表以解燃眉之急,后续还是要分表分库,当然这是后话.下面简要说一下将普通表转为分区表的步骤.   一.创建文件组 ...