收尾工作

在结束本章之前,我们还需要学一些很简单的小知识。仍然是用我们最熟悉的 String 结构体来举例吧。

析构函数

我们的 String 结构体貌似忘记了一件重要的事情。我们一直在不断地 new 分配空间,却从来没有释放过。那么问题来了,什么时候释放呢?

很显然,当一个 String 类型变量不能再用的时候,就应该释放它所管的那片内存。那什么叫“不能再用”呢?这里指的就是离开这个变量的存储期之时。回忆一下:

int main() {
    String a("Hi"); // a 初始化
    if (a.length() == 2) {
        String b(a); // b 初始化
        // [...]
    } // b 的存储期结束
} // a 的存储期结束

对于一般自动存储期的变量来说,退出外层复合语句(大括号)的时候,就离开了它的存储期。显然,这个时候就可以安全地释放这个变量所关联的内存空间了。析构函数(Destructor)就是为此而设计的。

析构函数也是一种特殊的成员函数。当一个变量离开它的存储期时,编译器会调用其析构函数,随后按照成员列表的逆序调用各个成员的析构函数。它的语法是:

~结构体名() {
    离开存储期时需要做的事
}

和默认构造函数很像,但前面要加一个波浪号 ~。析构函数无需标识返回值类型,不能重载(只能无参)。在我们的 String 结构体中,只需要:

~String() {
    delete[] str; // 释放 str 所指向的内存
}

即可。一般地,析构函数都是用于 delete 构造函数中 new 出来的内存。

执行析构序列(即调用析构函数和成员的析构函数)是生存期(Lifetime)结尾而非存储期(Storage duration)结尾发生的。但对象的生存期和存储期总是一致的,这里便不再区分。

当未声明析构函数时,编译器会生成预置析构函数——一个什么都不做的一个函数。所以你也可以对析构函数使用 =default=delete,但这并没什么意义。

公开、私有、class 关键字

在本章的最后,我们介绍 class 关键字。它本质上和 struct 关键字是完全一样的,所以我们直接做一个替换先:

class String {
    // [...]
};

当然 classstruct 肯定还是有区别的。其中一个区别就是,struct 的成员默认是公开的,而 class 的成员默认是私有的。举个例子:

struct A {
    int data; // 这是公开成员
};
class B {
    int data; // 这是私有成员
};
int main() {
    A a;
    a.data; // OK,公开成员可以用
    B b;
//  b.data; // 编译错误,data 成员不可访问
}

上面的例子表明:公开成员(Public member)指结构体中外界可以访问的成员,私有成员(Private member)指结构体中外界不可以访问的成员,但可以在结构体内部访问(比如成员函数中)。

class B {
    int data; // 私有成员
    void f() {
        data = 42; // 结构体内部可以访问
    }
};
int main() {
    B b;
//  b.data; // 一旦出了结构体,就不能访问了
}

struct 中,用 private: 来指示接下来的成员都是私有的:

struct A {
    int a; // 公开成员
    int b; // 公开成员
private: // 接下来开始都是私有的
    int c; // 私有成员
    int d; // 私有成员
};

而在 class 中,用 public: 指示接下来的成员都是公开的:

class B {
    int a; // 私有成员
    int b; // 私有成员
public:
    int c; // 公开成员
    int d; // 公开成员
};

public:private: 是三个访问说明符中的两个,它们事实上可以随意地在 structclass 的成员列表中插入;而 structclass 在访问性上的区别仅仅作用于成员列表开头的那几个成员。下一节的总结中,你将看到使用这些访问说明符后的 String 结构体。

最后的最后,我们将用词换一下:称运用了面向对象思想的结构体为(Class),称这样结构体的变量为对象(Object)。

最近更新:
代码未运行