本章介绍了定义和使用类的许多重要方面。其中的一些方面是非常微妙甚至很难理解的概念。如果其中的某些概念对于您来说过于复杂,也不用害怕——这些问题对于大多数C++的初学者来说都是很难的。通常,对于诸如复制构造函数等概念,都是在由于忽略它们而遇到了麻烦之后逐步理解的。本章介绍的一些内容乍看起来难以理解,但是随着经验越来越丰富,对其理解也将越透彻。
在类构造函数中,可以使用new为数据分配内存,然后将内存地址赋给类成员。这样,类便可以处理长度不同的字符串,而不用在类设计时提前固定数组的长度。在类构造函数中使用new,也可能在对象过期时引发问题。如果对象包含成员指针,同时它指向的内存是由new分配的,则释放用于保存对象的内存并不会自动释放对象成员指针指向的内存。因此在类构造函数中使用new类来分配内存时,应在类析构函数中使用delete来释放分配的内存。这样,当对象过期时,将自动释放其指针成员指向的内存。如果对象包含指向new分配的内存的指针成员,则将一个对象初始化为另一个对象,或将一个对象赋给另一个对象时,也会出现问题。在默认情况下,C++逐个对成员进行初始化和赋值,这意味着被初始化或被赋值的对象的成员将与原始对象完全相同。如果原始对象的成员指向一个数据块,则副本成员将指向同一个数据块。当程序最终删除这两个对象时,类的析构函数将试图删除同一个内存数据块两次,这将出错。解决办法是:定义一个特殊的复制构造函数来重新定义初始化,并重载赋值运算符。在上述任何一种情况下,新的定义都将创建指向数据的副本,并使心对像指向这些副本。这样,旧对象和新对象都将引用独立的、相同的数据,而不会重叠。由于同样的原因,必须定义复制运算符。对于每一种情况,最终目的都是执行深度复制,也就是说,复制实际的数据,而不仅仅是复制致橡树局的指针。对象的存储持续性为自动或外部时,在它不再存在时将自动条用其析构函数。如果使用new运算符为对象分配内存,并将其地址付给一个指针,则当您将delete用于该指针时将自动为对象调用析构函数。然而,如果使用定位new运算符(而不是常规new运算符)为类对象分配内存,则必须负责显式地为该对象调用析构函数,方法是使用指向该对象的指针调用西沟函数方法。C++允许在类中包含结构、类和枚举定义。这些潜逃类型的作用于为整个类,这意味着它们被局限于类中,不会与其他地方定义的同名结构、类和枚举发生冲突。C++为类构造函数提供了一种可用来初始化数据称源的特殊语法。这种语法包括冒号和由都好分割的初始化列表,被放在构造函数参数的右括号后,函数体的左括号之前。每一个初始化器都由被初始化的成员的名称和包含初始值的括号组成。从概念上来说,这些初始化操作是在对象创建时进行的,此时函数体中的语句还没有执行。语法如下:queue(int qs) : qsize(qs), items(0), front(NULL), rear(NULL) { }如果数据成员是非静态const成员或引用,则必须采用这种格式,单可将C++11新增的类内初始化用于非静态const成员。C++11允许类内初始化,即在类定义中进行初始化:class Queue{ private:... Node * front = NULL; enum {Q_SIZE = 10}; Node * rear = NULL; int items = 0; const int qsize = Q_SIZE;...}这与使用成员初始化列表等价。然而,使用成员初始化列表的构造函数将覆盖相应的类内初始化。与简单的C结构相比,需要注意的类细节要多得多。作为回报,它们的功能也更强。