收尾工作
在结束本章之前,我们还需要学一些很简单的小知识。仍然是用我们最熟悉的 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 {
// [...]
};
当然 class
和 struct
肯定还是有区别的。其中一个区别就是,struct
的成员默认是公开的,而 class
的成员默认是私有的。举个例子:
上面的例子表明:公开成员(Public member)指结构体中外界可以访问的成员,私有成员(Private member)指结构体中外界不可以访问的成员,但可以在结构体内部访问(比如成员函数中)。
class B {
int data; // 私有成员
void f() {
data = 42; // 结构体内部可以访问
}
};
int main() {
B b;
// b.data; // 一旦出了结构体,就不能访问了
}
在 struct
中,用 private:
来指示接下来的成员都是私有的:
而在 class
中,用 public:
指示接下来的成员都是公开的:
public:
和 private:
是三个访问说明符中的两个,它们事实上可以随意地在 struct
和 class
的成员列表中插入;而 struct
和 class
在访问性上的区别仅仅作用于成员列表开头的那几个成员。下一节的总结中,你将看到使用这些访问说明符后的 String
结构体。
最后的最后,我们将用词换一下:称运用了面向对象思想的结构体为类(Class),称这样结构体的变量为对象(Object)。