热度 16
2014-2-17 15:45
1254 次阅读|
5 个评论
类(Classes) 类(class)是一种将数据和函数组织在同一个结构里的逻辑方法。定义类的关键字为 class , 其功能与 C 语言中的 struct 类似 ,不同之处是 class 可以包含函数,而不像 struct 只能包含数据元素。(请注意这里是相对于C语言来说的,C++中struct 和 class关 键字类似,仅在于默认的访问权限不同) 类定义的形式是: class class_name { permission_label_1: member1; permission_label_2: member2; ... } object_name; 其中 class_name 是类的名称 (用户自定义的类型) ,而可选项 object_name 是一个或 几个对象(object)标识。Class 的声明体中包含成员 members,成员可以是数据或函数 定义,同时也可以包括允许范围标志 permission labels,范围标志可以是以下三个关键 字中任意一个:private:, public: 或 protected:。它们分别代表以下含义: •private :class 的 private 成员,只有同一个 class 的其他成员或该 class 的 “friend” class 可以访问这些成员。 •protected :class 的 protected 成员,只有同一个 cl ass 的其他成员,或该 class 的“friend” class,或该 class 的子类(derived classes) 可以访问这些成员。 •public :class 的 public 成员,任何可以看到这个 class 的地方都可以访问这 些成员。 如果我们在定义一个 class 成员的时候没有声明其允许范围,这些成员将被默认为 private 范围。 例如: class CRectangle { int x, y; public: void set_values (int,int); int area (void); } rect; 上面例子定义了一个 class CRectangle 和该 class 类型的对象变量 rect 。这个 class 有 4 个成员:两个整型变量 (x 和 y) ,在 private 部分 (因为 private 是默认的允许 范围);以及两个函数, 在 public 部分:set_values() 和 area(),这里只包含了函 数的原型(prototype)。 注意 class 名称与对象(object)名称的不同:在上面的例子中,CRectangle 是 class 名称 (即用户定义的类型名称),而 rect 是一个 CRectangle 类型的对象名称。它们的区 别就像下面例子中类型名 int 和 变量名 a 的区别一样: Int a; int 是 class 名称 (类型名) ,而 a 是对象名 object name (变量)。 在程序中,我们可以通过使用对象名后面加一点再加成员名称(同使用 C structs 一样), 来引用对象 rect 的任何 public 成员,就像它们只是一般的函数或变量。例如: rect.set_value (3,4); myarea = rect.area(); 但我们不能够引用 x 或 y ,因为它们是该 class 的 private 成员,它们只能够在该 class 的其它成员中被引用。晕了吗?下面是关于 class CRectangle 的一个复杂的例子: // classes xample #include class CRectangle { int x, y; public: void set_values (int,int); int area (void) {return (x*y);} }; void CRectangle::set_values (int a, int b) { x = a; y = b; } int main () { CRectangle rect; rect.set_values (3,4); cout "area: " rect.area(); } area: 12 上面代码中新的东西是在定义函数 set_values().使用的范围操作符(双冒号:: )。它是 用来在一个 class 之外定义该 class 的成员。注意,我们在 CRectangle class 内部已经 定义了函数 area() 的具体操作,因为这个函数非常简单。而对函数 set_values() ,在 class 内部只是定义了它的原型 prototype,而其实现是在 class 之外定义的。这种在 class 之外定义其成员的情况必须使用范围操作符::。 范围操作符 (::) 声明了被定义的成员所属的 class 名称,并赋予被定义成员适当的范 围属性,这些范围属性与在 class 内部定义成员的属性是一样的。例如,在上面的例子 中,我们在函数 set_values() 中引用了 private 变量 x 和 y,这些变量只有在 class 内部和它的成员中才是可见的。 在 class 内部直接定义完整的函数,和只定义函数的原型而把具体实现放在 class 外部 的唯一区别在于,在第一种情况中,编译器(compiler) 会自动将函数作为 inline 考虑, 而在第二种情况下,函数只是一般的 class 成员函数。 我们把 x 和 y 定义为 private 成员 (记住,如果没有特殊声明,所有 class 的成员均 默认为 private ),原因是我们已经定义了一个设置这些变量值的函数 (set_values()) ,这样一来,在程序的其它地方就没有办法直接访问它们。也许在一个 这样简单的例子中,你无法看到这样保护两个变量有什么意义,但在比较复杂的程序中, 这是非常重要的,因为它使得变量不会被意外修改 (这里意外指的是从 object 的角度 来讲的意外)。 使用 class 的一个更大的好处是我们可以用它来定义多个不同对象(object)。例如, 接着上面 class CRectangle 的例子,除了对象 rect 之外,我们还可以定义对象 rectb : // class xample #include class CRectangle { int x, y; public: void set_values (int,int); int area (void) {return (x*y);} }; void CRectangle::set_values (int a, int b) { x = a; y = b; } int main () { CRectangle rect, rectb; rect.set_values (3,4); rectb.set_values (5,6); cout "rect area: " rect.area() endl; cout "rectb area: " rectb.area() endl; } rect area: 12 rectb area: 30 注意: 调用函数 rect.area() 与调用 rectb.area()所得到的结果是不一样的。这是因为 每一个 class CRectangle 的对象都拥有它自己的变量 x 和 y,以及它自己的函数 set_value() 和 area()。 这是基于对象( object) 和 面向对象编程 (object-oriented programming)的概念的。 这个概念中,数据和函数是对象(object)的属性(properties),而不是像以前在结构化 编程 (structured programming) 中所认为的对象(object)是函数参数。在本节及后面 的小节中,我们将讨论面向对象编程的好处。 在这个具体的例子中,我们讨论的 class (object 的类型)是 CRectangle,有两个实例 (instance),或称对象(object):rect 和 rectb,每一个有它自己的成员变量和成员函 数。 构造函数和析构函数 (Constructors and destructors) 对象(object)在生成过程中通常需要初始化变量或分配动态内存,以便我们能够操作, 或防止在执行过程中返回意外结果。例如,在前面的例子中,如果我们在调用函数 set_values( ) 之前就调用了函数 area(),将会产生什么样的结果呢?可能会是一个不 确定的值,因为成员 x 和 y 还没有被赋于任何值。 为了避免这种情况发生,一个 class 可以包含一个特殊的函数:构造函数 constructor, 它可以通过声明一个与 class 同名的函数来定义。当且仅当要生成一个 class 的新的实 例 (instance)的时候,也就是当且仅当声明一个新的对象,或给该 class 的一个对象分 配内存的时候,这个构造函数将自动被调用。下面,我们将实现包含一个构造函数的 CRectangle : // class xample #include class CRectangle { int width, height; public: CRectangle (int,int); int area (void) {return (width*height);} }; CRectangle::CRectangle (int a, int b) { //构造函数没有返回值 width = a; height = b; } int main () { CRectangle rect (3,4); CRectangle rectb (5,6); cout "rect area: " rect.area() endl; cout "rectb area: " rectb.area() endl; } rect area: 12 rectb area: 30 正如你所看到的,这个例子的输出结果与前面一个没有区别。在这个例子中,我们只 是把函数set_values换成了class的构造函数constructor。注意这里参数是如何在class 实例 (instance)生成的时候传递给构造函数的: CRectangle rect (3,4); CRectangle rectb (5,6); 同时你可以看到,构造函数的原型和实现中都没有返回值(return value),也没有 void 类型声明。构造函数必须这样写。一个构造函数永远没有返回值,也不用声明 void,就 像我们在前面的例子中看到的。 析构函数 Destructor 完成相反的功能。它在 objects 被从内存中释放的时候被自动 调用。释放可能是因为它存在的范围已经结束了(例如,如果 object 被定义为一个函数 内的本地(local)对象变量,而该函数结束了);或者是因为它是一个动态分配的对 象, 而被使用操作符 delete 释放了。 析构函数必须与 class 同名,加水波号 tilde (~) 前缀,必须无返回值。 析构函数特别适用于当一个对象被动态分别内存空间,而在对象被销毁的时我们希望释 放它所占用的空间的时候。例如: // example on constructors and destructors #include class CRectangle { int *width, *height; public: CRectangle (int,int); ~CRectangle (); int area (void) {return (*width * *height);} }; CRectangle::CRectangle (int a, int b) { width = new int; height = new int; *width = a; *height = b; } CRectangle::~CRectangle () { delete width; delete height; } int main () { CRectangle rect (3,4), rectb (5,6); cout "rect area: " rect.area() endl; cout "rectb area: " rectb.area() endl; return 0; } rect area: 12 rectb area: 30 构造函数重载(Overloading Constructors) 像其它函数一样,一个构造函数也可以被多次重载(overload)为同样名字的函数,但有 不同的参数类型和个数。记住,编译器会调用与在调用时刻要求的参数类型和个数一样 的那个函数(Section 2.3, Functions-II)。在这里则是调用与类对象被声明时一样的那 个构造函数。