Skip to content

Chapter 3 Class

1 Point

C :数据和函数分开,通过普通函数操作对象
typedef struct point {
    int x;
    int y;
} Point;

// 通过 const Point* 传入对象地址,避免拷贝对象
void print(const Point* p) {
    // 通过指针访问结构体成员
    printf("%d %d\n", p->x, p->y);
}

void move(Point* p, int dx, int dy) {
    p->x += dx;
    p->y += dy;
}

Point a;
Point b;
a.x = b.x = 1;
a.y = b.y = 1;

move(&a, 2, 2);
print(&a);
print(&b);

C++ :对象把属性和行为封装在一起,调用形式更自然

class Point {
public:
    void init(int x, int y);
    void move(int dx, int dy);
    void print() const;

private:
    int x;
    int y;
};

void Point::init(int ix, int iy) {
    x = ix;
    y = iy;
}

void Point::move(int dx, int dy) {
    x += dx;
    y += dy;
}

void Point::print() const {
    cout << x << ' ' << y << endl;
}

Point a;
a.init(1, 2);
a.move(2, 2);
a.print();
Objects = Attributes + Services
  • Data:对象的属性、状态(properties / status)
  • Operations:对象提供的操作、功能(functions)

2 this 指针

每个非静态成员函数都有一个隐藏参数(hidden parameter)this,可以理解为指向当前对象的指针。

成员函数可看作带对象指针参数的普通函数

void Stash::initialize(int sz);

可以理解为:

void Stash::initialize(Stash* this, int sz);

调用成员函数时必须指定对象

Stash a;
a.initialize(10);

可以理解为:

Stash::initialize(&a, 10);

this:指向调用该函数的对象

  • 在成员函数内部,可以直接使用 this
  • this 指向“当前正在调用该成员函数的那个对象”
  • 它是成员函数天然拥有的局部变量,不能自己定义,但可以直接使用
对象(Object)
  • 在 C++ 中,对象本质上就是一个变量,其最纯粹的定义是一块存储区域(a region of storage)
  • 前面提到的结构体变量,在 C++ 中也都是对象

3 Ticket Machine Example

售票机问题描述

  • 当顾客投入了正确金额后,售票机会打印车票
  • 顾客先向机器中投入钱,再请求打印车票
  • 机器在运行过程中,会持续累计自己收到的总金额
面向过程(Procedure-Oriented)地描述买票流程
  1. Step to the machine
  2. Insert money into the machine
  3. The machine prints a ticket
  4. Take the ticket and leave

我们可以写一个程序去模拟买票流程这件事,这样的程序可以运行,但并没有真正表示出那台机器本身,因而难以继续扩展和进一步开发。

售票机应被看作一个对象,它包含以下内容:

class TicketMachine {
// 对外提供服务的接口
public:
    void showPrompt();
    void getMoney();
    void printTicket();
    void showBalance();
    void printError();

// 对象内部状态,不希望外部直接改动
private:
    const int PRICE;
    int balance;
    int total;
};
Object vs. Class
  • Objects:表示具体事物或事件,在运行时响应消息
  • Classes:定义实例有哪些属性,在 C++ 中扮演类型(type)的角色

OOP Characteristics
  1. Everything is an object.
  2. A program is a bunch of objects telling each other what to do by sending messages.
  3. Each object has its own memory made up of other objects.
  4. Every object has a type.
  5. All objects of a particular type can receive the same messages.

类的定义方式

  • 在 C++ 中,通常使用分离的 .h.cpp 文件来定义一个类
  • 类声明成员函数原型写在头文件(.h)中
  • 成员函数函数体写在源文件(.cpp)中
  • PImpl technique 有争议,主要作用是隐藏私有成员并减少编译依赖

4 Scope Resolution Operator ::

:: 解析符(resolver)

  • <ClassName>::<functionName>:表示某个类的成员函数定义或访问
  • ::<functionName>:表示全局作用域中的名字

作用域解析示例

void S::f() {
    ::f();   // 否则会递归调用成员函数 f()
    ::a++;   // 选择全局变量 a
    a--;     // 这里的 a 是类作用域中的 a
}

编译单元(Compilation unit)

  • 编译器每次只看一个 .cpp 文件,并生成一个 .obj 目标文件
  • 链接器(linker)再把所有 .obj 文件链接成一个可执行文件
  • 如果想让其他 .cpp 文件知道某个函数或类的信息,需要使用 .h 头文件提供声明

5 The Header Files

  • 如果一个函数声明在头文件里,那么:
    • 函数被使用的地方必须包含这个头文件
    • 函数被定义的地方也必须包含这个头文件
  • 如果一个类声明在头文件里,那么:
    • 类被使用的地方必须包含这个头文件
    • 类成员函数被定义的地方也必须包含这个头文件
Header = Interface
  • 头文件是你与代码使用者之间的一份契约(contract)
  • 编译器通过“先声明,后使用”的规则来强制执行这份契约
  • 所有结构、函数在使用前都必须先声明

6 Structure of C++ Program

C++ 程序的基本组织结构

  • .h 文件中放 declarations(声明)
  • .cpp 文件中放 definitions(定义)
  • .cpp 通过 #include.h 的内容插入进来
  • 预处理(after pre-compiler / preprocessor)后,头文件内容会被展开到对应的 .cpp
  • 其他模块如果要使用这些函数,也会通过 #include 包含同一个头文件

Declarations vs. Definitions
  • 一个 .cpp 文件就是一个 compile unit
  • .h 中不放普通定义,只放声明(declarations),如:
    • extern 变量声明
    • 函数原型(function prototypes)
    • class / struct 声明

#include

#include 会把被包含文件的内容插入到当前 .cpp#include 所在的位置

#include "xx.h"
#include <xx.h>
  • #include "xx.h":通常先在当前目录查找,具体行为与实现有关
  • #include <xx.h>:在编译器指定的目录中查找

标准头文件保护结构

#ifndef HEADER_FLAG
#define HEADER_FLAG

// Type declaration here...

#endif // HEADER_FLAG

头文件编写建议

  1. 一个头文件只放一个类声明
  2. 头文件名通常与对应的 .cpp 同名
  3. 头文件内容要用 #ifndef / #define / #endif 包围起来