- 浏览: 121834 次
- 性别:
- 来自: 武汉
文章分类
最新评论
对于一个非虚拟函数的调用,编译器在编译时刻选择被调用的函数,而虚拟函数调用的
决定则要等到运行时刻。在执行程序内部的每个调用点上,系统根据被调用对象的实际基类
或派生类的类型来决定选择哪一个虚拟函数。实例,例如考虑下面的代码
void init( IntArray &ia )
{
for ( int ix = 0; ix < ia.size(); ++ix )
ia[ ix ] = ix;
}
形式参数ia 可以引用IntSortedArray、IntArrayRC 或IntArray 类的对象。我们将简要介
绍这里的派生类函数,size()作为非虚拟函数由编译器处理并内联展开,但是下标操
作符要直到执行循环的每次迭代时才能被处理,因为在编译期间编译器不知道数组ia 指向的
实际类型。
第17 章将详细讨论虚拟函数,包括虚拟析构函数的主题以及使用虚拟函数设计带来
的效率问题,【LIPPMAN96a 】对虚拟函数的实现与效率有更深入的讨论。
一旦我们定好了设计方案,C++的实现就很容易了。例如下面这个完整的IntArrayRC
派生类定义被放在一个独立的头文件IntArrayRC.h 中,该文件包含头文件IntArray.h, 而
IntArray.h 包含有IntArray 类的定义。
#ifndef IntArrayRC_H
#define IntArrayRC_H
#include "IntArray.h"
class IntArrayRC : public IntArray {
public:
IntArrayRC( int sz = DefaultArraySize );
IntArrayRC( int *array, int array_size );
IntArrayRC( const IntArrayRC &rhs );
virtual int& operator[]( int );
private:
void check_range( int );
};
#endif
IntArrayRC 只需定义不同于IntArray 实现的那些方面,或者加上对IntArray 扩展的实现。
1 它必须提供自己的下标操作符实例,以支持范围检查
2 它必须提供一个操作来做实际的检查工作,由于它不是公有接口的一部分,所以我们
把它声明为private
3 它必须提供一组自动初始化函数,即自己的构造函数集。
IntArray 的成员函数与数据成员对于IntArrayRC 来说都是可用的,就如同IntArrayRC
已经显式地定义了它们一样。这正是下面这句话的含义
class IntArrayRC : public IntArray
冒号定义了IntArrayRC 是从IntArray 派生而来的。关键字public 表明派生类共享基类
的公有接口。IntArrayRC 类型的对象可以用在任何可以使用基类类型对象的位置上。比
如在swap()例子中。第18 章会详细解释这一点。IntArrayRC 可以看作是IntArray 的扩展。
它增加了下标范围检查的额外特性。下面是下标操作符的一个实现
inline int&
IntArrayRC::operator[]( int index )
{
check_range( index );
return ia[ index ];
}
这里check_range()被实现为一个内联成员函数,它调用assert()宏。关于assert()宏的讨
论见1.3 节
#include <cassert>
inline void
IntArrayRC::check_range( int index )
{
assert( index >= 0 && index < size );
}
我们把check_range()函数作为一个独立的函数,以便说明私有成员函数并且将范围检
查的处理封装起来,方便我们以后改变边界错误的处理方式或是用异常处理代替assert()。
派生类对象实际上由几部分构成,每个基类是一个类的子对象subobject,它在新定
义的派生类中有独立的一部分派生类。对象的初始化过程是这样的:首先自动调用每个基类的构造函数来初始化相关的基类子对象,然后再执行派生类的构造函数。从设计的角度来看,派生类的构造函数应该只初始化那些在派生类中被定义的数据成员,而不是基类中的数据成员。
虽然我们引入了与类相关的下标操作符版本,以及一个私有的check_range()辅助函数,
但是我们并没有引入需要初始化的额外数据成员,因此,我们可以合理地假设.继承基类的
构造函数已经足够了,我们不需要再提供IntArrayRC 的构造函数——因为不需要它们做任何事情。
但是,实际上我们还是需要提供IntArrayRC 的构造函数,因为基类的构造函数并没有
被派生类继承,析构函数和拷贝赋值操作符同样也没有,还因为我们需要某个接口以便
通过这个接口把必要的参数传递给基类IntArray 的构造函数。
例如,假设我们定义了一个IntArrayRC 对象
int ia[] = { 0, 1, 1, 2, 3, 5, 8, 13 };IntArrayRC iarc( ia, 8 );
怎样才能把ia 和8 传递给基类的构造函数呢?不可否认,如果IntArray 构造函数被继承
了,那么就没有这个问题。实际上,那样的话我们会有其他更严重的问题,但现在没有足够的
篇幅向你证明这一点,无论如何派生类构造函数的语法提供了向基类构造函数传递参数的
接口。
例如,下面是两个必需的IntArrayRC 构造函数,第14 章与第17 章将对构造函数作更
多的讲解。其中,包括关于为什么我们不需要提供IntArrayRC 拷贝构造函数的解释
inline IntArrayRC::IntArrayRC( int sz)
: IntArray( sz ) {}
inline IntArrayRC::IntArrayRC( const int *iar, int sz )
: IntArray( iar, sz ) {}
由冒号分割出来的部分称作成员初始化列表member initialization list,它提供了一种
机制,通过这种机制,我们可以向IntArray 的构造函数传递参数。两个IntArrayRC 构造函数
的函数体都是空的,因为它们的工作就是把参数传递给相关的IntArray 构造函数,我们无需
提供显式的IntArrayRC 析构函数,因为派生类没有引入任何需要析构的数据成员,继承过来
的需要析构的IntArray 成员都由IntArray 的析构函数来处理。
发表评论
-
在ubuntu7.10用终端编译运行c++程序
2008-02-27 15:54 1014<script>function StorePag ... -
函数指针大全
2009-01-01 18:44 507<script>function StorePag ... -
问题的解决
2009-05-20 00:16 504<script>function StorePag ... -
1.2 C++程序
2009-05-20 23:16 570<script>function StorePag ... -
C++的预处理器指示符
2009-05-21 22:58 892<script>function StorePag ... -
1.4 注释
2009-05-23 00:05 544<script>function StorePag ... -
1.5 输入输出初步
2009-05-23 23:18 551<script>function StorePag ... -
2.3 基于对象的设计
2009-05-27 22:17 581<script>function StorePag ... -
基于对象的设计(2)
2009-05-28 23:48 499<script>function StorePag ... -
基于对象的设计(3)
2009-05-30 00:51 521<script>function StorePag ... -
基于对象的设计(4)
2009-05-31 02:07 620<script>function StorePag ... -
基于对象的设计(5)
2009-05-31 22:50 673<script>function StorePag ... -
面向对象的设计(1)
2009-06-02 01:44 599<script>function StorePag ... -
面向对象的设计(2)
2009-06-03 00:22 659<script>function StorePag ... -
面向对象的设计(3)
2009-06-03 22:51 599<script>function StorePag ... -
面向对象的设计(5)
2009-06-05 23:17 726<script>function StorePag ... -
泛型设计(1)
2009-06-07 00:28 604<script>function StorePag ... -
泛型设计(2)
2009-06-08 01:04 671<script>function StorePag ... -
泛型设计(3)
2009-06-09 01:14 598<script>function StorePag ... -
基于异常的设计(1)
2009-06-10 00:39 532异常exception 是指在运行时刻程序出现的反情形,例如数 ...
相关推荐
面向对象设计是一种软件设计方法,它强调对象之间的交互和协作,以达到软件系统的高内聚、低耦合的目标。在软件设计中,面向对象设计方法可以帮助开发者更好地理解系统的需求和行为,从而提高软件系统的质量和可维护...
### UML面向对象设计基础 #### 基本概念与符号表示 《UML面向对象设计基础》一书详细介绍了面向对象软件设计的基础知识,包括基本概念、符号表示、术语、准则和原理。面向对象设计是一种软件设计范式,强调通过对象...
面向对象设计(Object-Oriented Design,OOD)是一种软件开发方法,它基于“对象”的概念,将现实世界的问题域转化为计算机程序。UML(Unified Modeling Language)是面向对象设计的一种标准化建模语言,用于可视化...
面向对象设计理论是软件开发领域中的核心概念,它是一种基于现实世界中对象和它们之间交互的编程模型。这种设计方法论极大地提高了代码的可维护性、可扩展性和重用性。下面将详细阐述面向对象设计的基本概念、原则...
面向对象设计模式是软件开发中的一种重要思想,它通过总结和提炼在软件设计过程中常见的问题和解决方案,形成了可复用的设计规范。标题提到的“23种面向对象设计模式”涵盖了设计模式的主要分类,这些模式在Java、...
"面向对象程序设计—图书管理系统设计" 面向对象程序设计是当前软件开发的主流技术之一,其应用范围非常广泛,涵盖了各个行业和领域。图书管理系统是图书馆中的一种重要管理系统,它负责管理图书的存储、借阅、归还...
面向对象设计是在分析的基础上进行的,它关注于如何将需求转化为软件系统的内部结构。设计阶段的输出通常包括更详细的类图、活动图、序列图、状态图和组件图等UML模型。设计过程还会确定系统组件的接口和协作方式。 ...
《面向对象设计原理与模式(Java版)》全面介绍了Java面向对象程序设计的原理和模式,帮助解决Java程序中的设计问题。此外,该书十分注重Java面向对象程序设计的每个细节,以及继承、方法、类、设计模式等在程序设计...
4. 继承:继承是面向对象设计中的一个核心机制,它允许创建一个新类来继承现有类的属性和方法。这使得软件设计可以复用和扩展。 5. UML(统一建模语言):UML是一种标准的图形化建模语言,用于软件系统的设计和文档...
面向对象程序设计 c++面向对象程序设计 c++面向对象程序设计
《面向对象程序设计:C++语言描述(原书第2版)》内容丰富,结构合理,写作风格严谨,深刻地论述了c++语言的面向对象编程的各种技术,主要内容包括:面向对象编程方法、c++语言的各种特性、stl、c++输入/输出流、mfc等。...
面向对象设计之3_基于UML的图书管理系统的分析与设计说明 本资源摘要信息主要介绍面向对象设计和UML(Unified Modeling Language)在图书管理系统中的应用。面向对象设计是一种程序设计方法,强调使用对象、类、...
面向对象设计模式是软件开发中的重要工具,它们是经过时间考验和广泛实践验证的设计解决方案,旨在提高代码的可重用性、灵活性和可维护性。C#作为一款强大的面向对象编程语言,为开发者提供了实现这些设计模式的良好...
C#面向对象设计模式纵横谈(4):Builder 生成器模式(创建型模式) C#面向对象设计模式纵横谈(5):Factory Method 工厂方法模式(创建型模式) C#面向对象设计模式纵横谈(6):Prototype 原型模式(创建型模式) C#面向...
面向对象程序设计课程设计题目 面向对象程序设计课程设计是一门旨在提高学生实际分析问题、编程和动手能力的课程设计。课程设计旨在引导学生学习掌握面向对象思想和 Java 编程语言,熟练运用 Java 工具,通过课程...
详细介绍了面向对象的分析与设计,全面探讨了面向对象概念、软件开发过程、UML和多层技术。本书使用最常见的技术和方法,通过一个贯穿全书的案例分析,对面向对象的软件开发过程和使用面向对象技术的编程过程进行了...