第七章-类

来源:互联网 发布:数组slice js 编辑:程序博客网 时间:2024/06/06 02:01


1 定义抽象数据类型

  • 类的接口包括用户所能执行的操作;类的实现则包括类的数据成员、负责接口实现的函数体以及类所需的各种私有函数。

1-1 设计Sales_data类

1-2 定义改进的Sales_data类

  • 当我们调用成员函数时,实际上是在替某个对象调用它。成员函数通过一个名为this的额外隐式参数来访问调用它的对象。当我们调用一个成员函数时,用请求该对象的对象地址初始化this。
  • 把const关键字放在成员函数的参数列表之后,此时,紧跟在参数列表后面的const表示this是一个指向常量的指针。

1-3 定义类相关的非成员函数

  • 一般来说,如果非成员函数是类接口的组成部分,则这些函数的声明应该与类在同一个头文件内。
  • 执行输出任务的函数应该尽量减少对格式的控制,这样可以确保由用户代码来决定是否换行。

1-4 构造函数

  • 当我们创建类的一个const对象时,直到构造函数完成初始化过程,对象才真正取得其“常量”属性。因此,构造函数在const对象的构造过程可以向其写值。
  • C++11新标准中,如果我们需要默认的行为,那么可以通过在参数列表后面写上=default来要求编译器生成默认构造函数。

1-5 拷贝、赋值和析构


2 访问控制与封装

  • 使用struct和class定义类唯一的区别就是默认的访问权限。

2-1 友元

  • 最好在类定义开始或结束前的位置集中声明友元。
  • 友元的声明仅仅指定了访问的权限,而非一个通常意义上的函数声明。如果我们希望类的用户能够调用某个友元函数,那么必须在友元声明之外再专门对函数进行一次声明。
  • 为了使友元对类的用户可见,通常把友元的声明与类本身放在同一个头文件中(类的外部)。


3 类的其他特性

3-1 类成员再探

  • 除了定义数据和函数成员之外,类还可以定义某种类型在类中的别名。由类定义的类型名字和其他成员函数一样存在访问限制,可以是public或private中的一种。用来定义类型的成员必须先定义后使用,这个普通成员有所区别,因此类型成员通常出现在类开始的地方。
  • 最好只在类的外部定义的地方说明成员函数是inline,这样更容易理解意图。
  • 有时(但不频繁)会发生这样一种情况,我们希望能修改类的某个数据成员,即使在一个const成员函数内。可以通过在变量的声明中加入mutable关键字做到这一点。
  • C++11新标准中,我们可以为类的数据成员指定一个类内初始值,让它们有个默认的值。

3-2 返回*this的成员函数

  • 一个const成员函数如果以引用的形式返回*this,那么它的返回类型将是常量引用。
  • 通过区分成员函数是否是const的,可以对其进行重载。

3-3 类类型

  • 前向声明引入了不完全类型,而不完全类型只能在非常有限的情景下使用:可以定义指向这种类型的指针或引用,也可以声明(但不可以定义)以不完全类型作为参数或返回类型的函数。

3-4 友元再探


4 类的作用域

  • 函数的返回类型通常出现在函数名之前。因此当成员函数定义在类外部时,返回类型中使用的名字都位于类的作用域之外。

4-1 名字查找与类的作用域

  • 类的定义分两步:首先,编译成员的声明;直到类全部可见后才编译函数体。
  • 类成员声明中使用的名字,包括返回类型或者参数列表中使用的名字,都必须在使用前确保可见。
  • 一般来说,内层作用域可以重新定义外层作用域中的名字,即使该名字已经在内层作用域中使用过。然而在类中,如果成员使用了外层作用域中的某个名字,而该名字代表一种类型,则类不能再之后重新定义该名字。
  • 成员函数中使用的名字按照如下方式解析:
  • 首先,在成员函数内(包括参数列表)查找该名字的声明。和正常查找一样,只有在该名字使用之前出现的声明才考虑。
  • 如果在成员函数内没有找到,则在类内继续查找,这时类的所有成员都可以被考虑。
  • 如果类内也没有找到该名字的声明,在成员函数定义之前的作用域内继续查找。


5 构造函数再探

5-1 构造函数初始值列表

  • 随着构造函数体一开始执行,数据成员初始化就完成了。
  • 如果数据成员是const、引用或者属于某种未提供默认构造函数的类类型,必须通过构造函数初始值列表为这些成员提供初始值(或者提供类内初始值)。
  • 数据成员的初始化顺序与它们在类定义中出现的顺序一致。

5-2 委托构造函数

5-3 默认构造函数的作用

5-4 隐式的类类型转换

  • 编译器只会自动地执行一步类型转换。
  • 在要求隐式转换的程序上下文中,可以通过将构造函数声明为explicit加以阻止。而explicit只对一个实参的构造函数有效(多实参的构造函数不能用于执行隐式转换,故不需要指定为explicit)。
  • 发生隐式转换的一种情况是当我们执行拷贝形式的初始化时(使用=)。此时,我们只能使用直接初始化而不能使用explicit构造函数。

5-5 聚合类

  • 聚合类需要满足以下条件:所用成员都是public;没有定义任何构造函数;没有类内初始值;没有基类,也没有virtual函数。
  • 我们可以提供一个花括号括起来的成员初始值列表,并用它初始化聚合类的数据成员。

5-6 字面值常量类

  • 除了算数类型、引用和指针外,某些类也是字面值类型。
  • 数据成员都是字面值类型的聚合类是字面值常量类。如果一个类不是聚合类,但是它符合以下要求,则它也是一个字面值常量类:
  • 数据成员必须是字面值类型;
  • 类必须至少包含一个constexpr构造函数;
  • 如果一个数据成员含所有类内初始值,则内置类型成员的初始值必须是一条常量表达式;或者如果成员属于某种类类型,则初始值必须使用成员自己的constexpr构造函数;
  • 类必须使用析构函数的默认定义,该成员负责销毁类的对象。


6 类的静态成员

  • 我们不能在类的内部初始化静态成员。必须在类的外部定义和初始化每一个静态成员。
  • 如果某个静态成员的应用场景仅限于编译器可以替换它的值的情况,则一个初始化的const或constexpr static不需要分别定义。但是,如果我们将它用作于值不能替换的场景中,则该成员必须有一条定义语句。
  • 即使一个常量静态数据成员在类内部被初始化了,通常情况也应该在类的外部定义一下该成员。
  • 类的静态数据成员可以是不完全类型。
  • 可以使用静态数据成员作为默认实参(而普通数据成员或者说局部变量不可以作为默认实参)。


0 0