2.3 类
上面这种将数据与其操作分离的做法有其优势,比如我们可以非常自由地使用它的数据部分。不过对于用户自定义类型来说,为了将其所有属性捏合在一起,形成一个“真正的类型”,在其表示形式和操作之间建立紧密的联系还是很有必要的。特别是,我们常常希望自定义的类型易于使用和修改,希望数据具有一致性,并且希望表示形式最好对用户是不可见的。此时,最理想的做法是把类型的接口(所有代码都可使用的部分)与其实现(对外部不可访问的数据具有访问权限)分离开来。在C++中,实现上述目的的语言机制称为类(class)。类含有一系列成员(member),可能是数据、函数或者类型。类的public成员定义了该类的接口,private成员则只能通过接口访问。例如:
在此基础上,我们可以定义一个Vector类型的变量:
下图解释了这个Vector对象的含义:
总的来说,Vector对象是一个“句柄”,它包含指向元素的指针(elem)以及元素的数量(sz)。在不同Vector对象中元素的数量可能不同(本例是6),即使同一个Vector对象在不同时刻也可能含有不同数量的元素(见4.2.3节)。不过,Vector对象本身的大小永远保持不变。这是C++语言处理可变数量信息的一项基本技术:一个固定大小的句柄指向位于“别处”(如通过new分配的自由存储,见4.2.2节)的一组可变数量的数据。第4章的主题就是学习如何设计并使用这样的对象。
在这里,我们只能通过Vector的接口访问其表示形式(成员elem和sz)。Vector的接口是由其public成员构成的,包括Vector()、operator和size()。2.2节的read_and_sum()示例可简化为:
与所属类同名的“函数”称为构造函数(constructor),即它是用来构造类的对象的。因此构造函数Vector()替换了2.2节的vector_init()。构造函数有一个特性与普通函数不同,它确保只用于初始化类的对象,因此定义一个构造函数可以解决类变量未初始化问题。
Vector(int)规定了Vector对象的构造方式,此处意味着需要一个整数来构造对象,这个整数用于指定元素的数量。该构造函数使用成员初始值列表来初始化Vector的成员:
这条语句的含义是:首先从自由存储获取s个double类型的元素,用一个指向这些元素的指针初始化elem;然后使用s初始化sz。
访问元素的功能是由一个下标函数提供的,它叫做operator[],它的返回值是元素的引用(double&)。
size()函数的作用是向使用者提供元素的数量。
显然,在上面的代码中完全没有涉及错误处理,与之有关的内容将在3.4节提及。同样我们也没有提供一种机制来“归还”通过new获取的double数组,4.2.2节将介绍如何使用析构函数来完成这一任务。
我们常用的两个关键字struct和class没有本质区别,唯一的不同是struct的成员默认是public的。例如,我们也可以为struct定义构造函数和其他成员函数,这一点与class完全一致。