目录
10.1 过程性编程和面对对象编程
- 最重要的OOP特性:抽象;封装和数据隐藏;多态;继承;代码的可重用性。
- 采用OOP方法时,首先从用户的角度考虑对象。
10.2 抽象和类
10.2.1 类型是什么
- 在C++中,用户定义类型指的是实现抽象对象的类设计。
- 指定基本类型完成了三项工作:决定数据对象需要的内存数量;决定如何解释内存中的位;决定可使用数据对象执行的操作或方法。
10.2.2 C++中的类
- 类是一种将抽象转换为用户定义类型的C++工具,它将数据表示和操纵数据的方法组合成一个整洁的包。
- 类规范由两个部分组成:类声明(以数据成员的方式描述数据部分,以成员函数的方式描述公有接口);类方法定义(描述如何实现类成员函数)。
- 一个约定俗成的约定:将类名首字母大写。
- 成员函数可以就地定义,也可以用原型定义。
- 关键字private和public描述了对类成员的访问控制,使用类对象的程序都可以直接访问公有部分,但只能通过公有成员函数来访问对象的私有成员。
- 数据项通常放在私有部分,组成类接口的成员函数放在公有部分。
- 不必在类声明中使用关键字private,因为这是类对象的默认访问控制。
- 类和结构的区别:结构的默认访问类型是public,而类为private。通常使用类来实现类描述,而把结构限制为只表示纯粹的数据对象或没有私有部分的类。
10.2.3 实现类成员函数
- 类成员函数两个特殊的特征:定义成员函数时,使用作用域解析操作符来识别函数所属的类;类方法可以访问类的private部件。
- 类作用域。限定名;非限定名。
- 最好的方法:将类声明放在头文件中,而将类成员函数定义放在单独的一个源代码文件中。
- 何时将某个函数声明为私有成员函数???
- 其定义位于类声明中的函数都将自动成为内联函数,类声明常将短小的成员函数作为内联函数。也可以在类声明之外定义成员函数,并使其成为内联函数,只需在类实现部分中定义函数时使用inine限定符即可。
- 调用成员函数时,它将使用被用来调用它的对象的成员函数。
- 所创建的每个新对象都有自己的存储空间,用于存储其内部变量和类成员;但同一个类的所有对象共享同一组类方法,即每种方法只有一个副本。
10.2.4 使用类
- C++的目标是使得使用类与使用基本的内置类型尽可能相同。
- 客户/服务器模型
10.2.5 小结
- 公有部分的内容构成了设计的抽象部分-公有接口。
- 在类声明和类方法之外使用该名称时,需要使用作用域解析操作符进行限定。
10.3 类的构造函数和析构函数
10.3.1 声明和定义构造函数
- 类构造函数,专门用于构造新对象,将值赋给它们的数据成员。
- 构造函数的原型和函数头都有一个有趣的特征,虽然没有返回值,但没有被声明为void类型,实际上,构造函数没有声明类型。
- 程序声明构造函数时,将自动调用构造函数。
- 构造函数的参数表示的不是类成员,而是赋给类成员的值,因此,参数名不能与类成员相同。为避免这种混乱,通常的做法是在数据成员名中使用m_前缀。
10.3.2 使用构造函数
- 显式调用构造函数 Stock food = Stock("World Cabbage", 250, 1.25);
- 隐式调用构造函数 Stock food("World Cabbage", 250, 1.25);
- 无法使用对象来调用构造函数,因为在构造函数构造出对象之前,对象是不存在的,因此构造函数被用来创建对象,而不能通过对象来调用。
10.3.3 默认构造函数
- 默认构造函数是在未提供显式的初始化值时,被用来创建对象的构造函数。
- 默认构造函数没有参数,因为声明中不包含值。
- 如果要创建对象,而不显式地初始化,则必须定义一个不接受任何参数的默认构造函数。
- 定义默认构造函数的方式有两种,一种是给已有构造函数的所有参数提供默认值,另一种方式是通过函数重载来定义另一个构造函数。
10.3.4 析构函数
- 如果构造函数使用new来分配内存,则析构函数将使用delete来释放这些内存,如果没有使用new,只需让编译器生成一个什么都不做的隐式析构函数即可。
- 析构函数没有返回值和声明类型,析构函数没有参数。
- 由于在类对象过期时析构函数将自动被调用,因此必须有一个析构函数,如果程序员没有提供析构函数,编译器将隐式地声明一个默认析构函数,并在发现导致对象被删除的代码后,提供默认构造函数的定义。
10.3.5 改进Stock类
10.3.6 构造函数和析构函数小结
- 如果构造函数只有一个参数,则将对象初始化为一个与参数的类型相同的值时,该构造函数将被调用。
- 对于未被初始化的对象,程序将使用默认构造函数来创建。
10.4 this指针
- 每个成员函数(包括构造函数和析构函数)都有一个this指针。this指针指向调用对象。如果方法需要引用整个调用对象,则可以使用表达式*this。在函数的括号后面使用const限定符将this限定为const,这样将不能使用this来修改对象的值。
- 一般来说,所有的类方法都将this指针设置为调用它的对象的地址。
- 因为这个对象作为函数参数传递,所以显式地访问;因为这个对象调用类成员函数,所以隐式地访问。
- const Stock & topval ( const Stock & s ) const;该函数隐式地访问一个对象,而显式地访问另一个对象,并返回其中一个对象的引用。括号中的const表明,该函数不会修改被显式地访问的对象;而括号后的const表明,该函数不会修改被隐式地访问的对象。由于该函数返回了两个const对象之一的引用,因此返回类型也应为const引用。
10.5 对象数组
- 当程序创建未被显式初始化的类对象时,总是调用默认构造函数。
- 可以用构造函数来初始化数组元素,在这种情况下,必须为每个元素调用构造函数;如果类包含多个构造函数,则可以对不同的元素使用不同的构造函数。
- 初始化对象数组的方案是,首先使用默认构造函数创建数组元素,然后花括号中的构造函数将创建临时对象,然后将临时对象的内容复制到相应的元素中。因此,要创建类对象数组,则这个类必须有默认构造函数。
10.6 接口和实现小结
- 类接口:公有???
- 修改类的私有部分和实现文件属于实现变更;修改类的公有部分属于接口变更。实现变更改变了类的内部工作原理,接口变更改变了使用类的人可用的编码方式。
10.7 类作用域
- 类作用域意味着不能从外部直接访问类的成员,公有成员函数也是如此。也就是说,要调用公有成员函数,必须通过对象。
- 作用域为整个类的常量:第一种方式是在类中声明一个枚举(用这种方式声明枚举并不会创建类数据成员,所有对象中都不包含枚举);另一种方式是使用关键字static,用此方法创建的静态常量将与其他静态变量存储在一起,而不是存储在对象中,只能使用这种技术声明值为整数或枚举的静态常量,而不能存储double常量。
10.8 抽象数据类型
- ADT以通用的方式描述数据类型,而没有引入语言或实现细节。
- 类概念非常适合于ADT方法。
- 仔细选择数据类型,使类最小。
- 类很适合用于描述ADT。公有成员函数接口提供了ADT描述的服务,类的私有部分和类方法的代码提供了实现,这些实现对类的客户隐藏。