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