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. ZBrush实用插件ZAppLink简介

    ZAppLink是ZBrush版本推出时被评为最值得期待的插件.事实证明,ZAppLink的出现让工具与工具之间有了交流,搭起软件与软件的沟通桥梁. ZAppLink插件专用于扩展ZBrush®的绘制 ...

  2. vue 事件参数传$event打印当前组件

    <template> <div class="hello"> <button v-on:click.once="getinfo($event ...

  3. 通过HttpURLConnection 上传和下载文件(二)

    HttpURLConnection文件上传 HttpURLConnection采用模拟浏览器上传的数据格式,上传给服务器 上传代码如下: package com.util; import java.i ...

  4. MySQL锁定状态查看命令

    1 show processlist; SHOW PROCESSLIST显示哪些线程正在运行.您也可以使用mysqladmin processlist语句得到此信息.如果您有SUPER权限,您可以看到 ...

  5. js 判断是否滚动到底部

    $(window).scroll(function(){ var scrollTop = $(this).scrollTop(); //scrollTop() 方法返回或设置匹配元素的滚动条的垂直位置 ...

  6. Qt之滚动字幕

    简述 滚动字幕,也就是传说中的跑马灯效果. ​简单地理解就是:每隔一段时间(一般几百毫秒效果较佳)显示的文字进行变化(即滚动效果). 简述 实现 效果 源码 实现 利用定时器QTimer,在固定的时间 ...

  7. 一个leetcode解题报告类目,代码很简洁

    http://bookshadow.com/leetcode/ 里面的代码很简洁.可以看.

  8. Win7操作系统防火墙无法关闭的问题 无法找到防火墙关闭的地方的解决的方法

    计算机右键-管理-服务和应用程序-服务,找到Windows Firewall.双击,启动类型设为自己主动,确定.若这不到这项服务说明被阉割.考虑更换介质安装系统.360等一些安全软件带也有防火墙.全然 ...

  9. Java之旅(二)--- ServletContext

     什么是ServletContext?  WEB容器在启动时,它会为每一个WEB应用程序都创建一个相应的ServletContext对象.它代表当前web应用.通过使用这个对象,servlet能够 ...

  10. Linux在中国的没落

    6月23日,Linux kernel 4.1(LTS)公布.在国际自由软件世界引起热烈反响. 反观我们国内,官方机构没有不论什么动静:在民间,Linux激情已经消失.与十几年前相比.Linux在国内已 ...