第七章 类

7.0 前言

  1. 类的基本思想: 数据抽象(data abstraction)封装(encapsulation)
  2. 数据抽象:是一种依赖于 接口(interface)实现(implementation) 分离的编程(以及设计)技术。
  3. 类的接口: 包含了用户所能执行的操作。
  4. 类的实现: 包含了类的数据成员,负责接口实现的函数体以及定义类所需的各种私有函数。
  5. 封装: 实现了类的接口和实现的分离,隐藏了类的实现细节,使得用户只能使用类的接口而无法查看其实现细节。

7.1 定义抽象数据类型

  1. 成员函数(member function): 声明写在类定义内部的函数,而定义写在类内外都可以。但是写在类定义外的函数名前必须加上类名。
    例如:
class Stu
{
  public :
  void display( );
  //内部定义成员函数
  void show( ){
  }
  Stu& ret();
  private:
  string name;
  unsigned age;
};
//外部定义成员函数
//注意在函数前要添加 类名:: 
void Stu::display( )
{
  cout << " name: " << name << " age: " << age;
}
  1. 运算程序的人称为用户(user),而使用别人定义的自定义类的程序员其实也是用户。
  2. 注意:成员函数默认都是隐式的内联(inline)函数。
  3. 当我们调用成员函数时,实际上是在替某个对象调用它。成员函数通过一个关键字 this ,同时也是 所有成员函数的隐式参数 来访问调用它的那个对象。 当我们调用一个成员函数时,用请求该函数的对象地址初始化 this 。
    例如调用上例成员函数 display 。
Stu Stu1;//定义一个 Stu1 对象
display();

则编译器负责将 Stu1 的地址传递给 display 的隐藏形参 this ,可以等价地理解为编译器将该调用重写成了如下形式:

display(&Stu1);//其中的 name 和 age 可以理解成写成了 Stu1.name 和 Stu1.age 
  1. this 关键字: 通常用于访问对象,而不是单访问对象的某个成员

① this 是 隐式定义 的;
默认情况下, this 是一个指向当前对象常量指针,它的类型是指向非常量版本 class 类型的常量指针 (是 top-level const ,但不是 low-level const );
但是在成员函数的参数列表后面添加 const ,则可以修改 this 的类型为指向常量版本 class 类型的常量指针 (既是 top-level const ,也是 low-level const )。

  1. 常量成员函数(const member function): 在参数列表后添加 const 的成员函数。
  2. 编译器分两步处理 class 类型:

①首先编译成员的声明;
②然后才编译成员函数体。

  1. 由第 7 点可知道, 成员函数体可以随意使用类中的其他成员而无需在意这些成员出现的次序
  2. 与其他函数一样,在类定义外定义的成员函数时,成员函数的函数头必须与类定义内的函数声明包含相同的返回类型、参数列表、函数名,同时还要在函数名前添加 类名:: (这是为了告诉编译器该方法是属于类定义的作用域内的)。另外,常量成员函数还要在参数列表后面添加 const 关键字。
  3. 常量对象,以及常量对象的引用和指针都只能调用常量成员函数。
  4. 定义一个返回 this 指向的对象的函数,例如:
Stu& Stu::ret()
{
  return *this;  //返回调用该函数的对象
}
  1. 一般来说,如果非成员函数是类接口的组成部分(概念上属于类但实际上并不是成员函数),则这些函数的声明应该与类在同一个头文件内。
  2. 默认情况下,拷贝类的对象其实是拷贝该对象的数据成员。
  3. 构造函数(constructor): 类用于初始化对象的成员函数。
  4. 构造函数具有以下特点:
  1. 没有返回类型;
  2. 函数名与类名相同;
  3. 与其它函数一样具有一个参数列表和函数体(两者可能为空);
  4. 可以有多个;
  5. 不能被声明成常量成员函数。
class CExample {
public:
    int a;
    float b;
    CExample()
    {
        a=0;
        b=8.8;
    }
};
  1. 默认构造函数(default constructor): 实现对象的默认初始化的特殊构造函数,默认构造函数 没有形参 ,当类 对象创建时自动执行
  2. 只有当程序员的类没有显式定义构造函数,编译器才会自动为其隐式定义一个默认构造函数。一旦定义了类的构造函数,则编译器不会自动为其定义默认构造函数。此时需要程序员自己定义默认构造函数。
  3. 合成的默认构造函数(synthesized default constructor): 编译器定义的构造函数。
  4. 对于绝大多数类,这个合成的默认构造函数将按照如下规则初始化类的数据成员:
  • 如果类内成员变量存在了初始值,则用他来初始化成员变量
  • 否则,默认初始化该成员
  1. 所以, 编译器有时候不能为某些类定义默认的构造函数 。比如当类中某些成员的类型没有默认构造函数,此时编译器将无法初始化该成员。
  2. 构造函数初始值列表(constructor initialize list)冒号以及冒号和花括号之间的代码 ,用于为新创建的对象的一个或几个数据成员赋初值。构造函数初始值是成员变量名字的一个列表,每个名字后面紧跟着括号括起来的(或者在花括号内的)成员初始值。不同成员的初始化通过逗号分隔开。
    例如,下面的构造函数与上面的构造函数的结果是相同的。
class CExample {
public:
    int a;
    float b;
    //构造函数初始化列表
    CExample(): a(0),b(8.8)
    {}  //因为构造函数只是为了初始化类成员,所以这里的函数体为空
};
  1. 如果构造函数存在没有被显式初始化的类成员,则这些类成员将会被默认初始化。
  2. 注意: C++ 初始化类成员时,是按照声明的顺序初始化的,而不是按照出现在初始化列表中的顺序。
  3. 同样的,构造函数也可以在类外部定义,也要在构造函数名前添加 类名::
  4. 一般来说如果我们不主动定义,编译器将会对类的每个成员合成初始化、拷贝、赋值和销毁操作。 只是对于某些类来说合成的操作无法正常执行,比如管理动态内存的类通常不能依赖于上述合成的操作,但是如果管理动态内存的类使用的是 vector 或 string 则编译器合成的操作可以正常执行。

7.2 访问控制与封装

7.3 类的其他特性

7.4 类的作用域

7.5 构造函数再探

7.6 类的静态成员

你可能感兴趣的:(第七章 类)