270 likes | 432 Views
class Y : public Object { private: int y; int z; public: Y(int a = 0, int b = 0) : y(a), z(b) {} void SetY(int a) { y = a; } void SetZ(int a) { z = a; } int GetY() { return y; } int GetZ() { return z; } virtual int IsEqual(Object&); virtual void Show(); };.
E N D
class Y : public Object { private: int y; int z; public: Y(int a = 0, int b = 0) : y(a), z(b) {} void SetY(int a) { y = a; } void SetZ(int a) { z = a; } int GetY() { return y; } int GetZ() { return z; } virtual int IsEqual(Object&); virtual void Show(); };
// TEST.CPP #include <iostream.h> #include "test.h" int X :: IsEqual(Object& rObj) { X &aX = (X&)rObj; return(aX.x == x); } void X :: Show() { cout << "X = " << x << endl; }
int Y :: IsEqual(Object& rObj) { Y &aY = (Y&)rObj; return(aY.y == y && aY.z == z); } void Y :: Show() { cout << "Y = " << y << '\t' << "Z = " << z << endl; }
输出: X = 1 Y = 10 Z = 20 X = 2 Y = 20 Z = 40 X = 3 Y = 30 Z = 60 X = 4 Y = 40 Z = 80 // TESTARR.CPP #include "array.h" #include "test.h" void main() { Array aArr(10); X *px; Y *py; for(int i = 0; i < 4; i ++) { px = new X(i + 1); py = new Y((i + 1) * 10, (i + 1) * 20); aArr.Append(px); aArr.Append(py); } aArr.Show();
输出: X = 1 Y = 10 Z = 20 X = 2 Y = 20 Z = 40 X = 3 X = 100 Y = 30 Z = 60 X = 4 Y = 40 Z = 80 px = new X(100); aArr.Insert(px, 5); aArr.Show(); py = (Y*)aArr.Delete(6); aArr.Show(); py->Show(); delete py; } 输出: X = 1 Y = 10 Z = 20 X = 2 Y = 20 Z = 40 X = 3 X = 100 X = 4 Y = 40 Z = 80 习题: 25, 26
11.3 静态成员 类实际上就是一种用户定义的数据类型。每当生成一个某一类的对象时,系统就为该对象分配一块内存来存放其所有成员(从理论上讲,包括所有成员函数)。然而,在实际应用中,常常希望程序中所有同一类的对象共享某一成员(通常指数据成员,比如,同一学校中所以学生对象中的学校名称成员),以保证数据的一致性及简化操作。解决这一问题的一个办法就是将需要共享的数据说明成全局变量,但这样作将破坏数据的封装性。更好的解决办法就是将需要共享的成员说明成类中的静态成员。
11.3.1静态数据成员 静态数据成员是指在类中说明的、用关键字 static 修饰的数据成员。例: class X { int d; static int s; // 引用性说明 //… }; int X :: s; // 定义性说明 在类中说明的静态成员属于引用性说明,必须在类外对其进行定义性说明(说明时还可以对其进行初始化,定义性说明时不得带关键字static)。所以需要这样做是因为静态成员不属于任何一个对象,而为所有同类对象所共享。
// SDCLS.H class X { int d; static int nCount; public: X(int a = 0) : d(a) { nCount ++; } ~X() { nCount --; } void Set(int a) { d = a; } int Get() { return d; } int Count() { return nCount; } }; int X :: nCount = 0;
// TSDCLS.CPP #include <iostream.h> #include "sdcls.h" void main() { X xArr[3], aX(100); int i; for(i = 0; i < 3; i ++) xArr[i].Set(i * i); for(i = 0; i < 3; i ++) cout << "xArr[" << i << "].d = " << xArr[i].Get() << '\t';
cout << "xArr[" << i << "].nCount = " << xArr[i].Count() << endl; } cout << "aX.d = " << aX.Get() << '\t'; cout << "aX.nCount = " << aX.Count() << endl; } 该程序的输出为: xArr[0].d = 0 xArr[0].nCount = 4 xArr[1].d = 1 xArr[1].nCount = 4 xArr[2].d = 4 xArr[2].nCount = 4 aX.d = 100 aX.nCount = 4
从上例的输出可以看出:无论哪个对象,其成员 nCount 的值均是相同的。实际上,所有对象中的该成员根本就是同一个变量。 本例中该变量的值为 4 是类定义和所创建的对象个数所决定的:这里将 nCount 设计成了一个对象计数器,其初值为 0。每创建一个对象其值就加一(参见类 X 的构造函数);而每当一个对象被撤消其值就减一(参见类 X 的析构函数)。 由于 nCount 是一个所有 X 类对象共享的数据成员,从理论上讲它不属于任何一个对象。因此,对该成员的访问以如下的形式进行将显得更为合理: n = X :: Count(); cout << X :: Count() << endl;
11.3.2静态成员函数 与静态数据成员相同,静态成员函数是类中说明的、用关键字 static 修饰的成员函数。 一般讲,若类中存在静态数据成员,则访问该数据成员的成员函数应当说明成静态的。以便为共享的数据提供共享的接口。 应当说明的是: 1. 由于关键字 static 不是数据类型的组成部分,所以在类外定义静态成员函数时不需要该关键字。 2. 由于静态成员函数没有 this 指针,所以对于那些需要访问类中其它非静态成员的静态成员函数,必须带有一个同类对象或引用参数。 3. 静态成员函数只能访问类中的静态数据成员(除非函数带有相应的参数),而非静态成员函数则可以访问类中的任何成员。
// SFCLS.H class X { int d; static int nCount; public: X(int a = 0) : d(a) { nCount ++; } ~X() { nCount --; } void Set(int a) { d = a; } int Get() { return d; } static int Count() { return nCount; } }; int X :: nCount = 0;
// TSFCLS.CPP #include <iostream.h> #include "sfcls.h" void main() { cout << X :: Count() << endl; X aX(100); cout << "aX.d = " << aX.Get() << '\t' << X :: Count() << endl; } 程序的输出为: 0 aX.d = 100 1
11.4 const,volatile对象和成员函数 对象 key_word class_name obj_name; 函数 type func_name(<arg_list>) key_word; 其中:key_word 或者是 const,或者是 volatile。用前者修饰的对象或成员函数叫做const(常)对象或成员函数;用后者修饰的对象或函数叫做 volatile(易变)对象或成员函数。通过常对象只能访问类中的常成员函数;通过易变对象只能访问类中的易变成员函数。 由于易变对象和成员函数很少使用,下而仅举例说明常对象和成员函数的作用。
// STRCLS.H #if !defined _STRCLS_H_ #define _STRCLS_H_ class String { private: char* pStr; int nLen; char* CopyStr(char*); public: String() : pStr(0), nLen(0) {} String(String&); String(char*);
~String() { delete []pStr; } char* IsIn(char) const; const char* GetContent() { return (const char*)pStr; } int Length() { return nLen; } void Show() const; }; #endif
// STRCLS.CPP #include <iostream.h> #include <string.h> #include "strcls.h" char* String :: CopyStr(char* s) { nLen = strlen(s); pStr = new char[nLen + 1]; strcpy(pStr, s); }
String :: String(String& rs) { CopyStr(rs.pStr); } String :: String(char* s) { CopyStr(s); }
char* String :: IsIn(char ch) const { char *cp = pStr; while(*cp && *cp != ch) cp ++; if(*cp == ch) return cp; return 0; } void String :: Show() { cout << pStr << endl; }
// TESTSTR.CPP #include <iostream.h> #include "strcls.h" void main() { const String cStr("A constant object"); String Str("A normal object"); cout << cStr.Length() << endl; // 错误 cout << Str.Length() << endl; // 输出 13 cStr.Show(); Str.Show(); }
从上例可以看出:通过常对象只能调用类中公有的常成员函数,而通过普通对象则可以调用类中的任何公有成员函数。类中只所以说明常成员函数就是为了方便常对象来调用它。从上例可以看出:通过常对象只能调用类中公有的常成员函数,而通过普通对象则可以调用类中的任何公有成员函数。类中只所以说明常成员函数就是为了方便常对象来调用它。 在许多场合下,需要使用常对象。比如,将一个对象用作一 个函数的参数,为了防止函数中由于偶然的因素修改了对象的内容,就将该参数说明成 const 的。但是,若类中没有说明常成员函数,则函数将无法通过该参数来访问对象中的任何成员。从另一个角度来看,使用常对象常常也就是不希望修改对象的内容。因此,常成员函数通常要设计成不改动类中数据成员的形式。比如,本例中的两个常成员函数一个中用来输出字符串的内容(Show( ) 函数),而另一个是测试一 个字符是否存在于串中(IsIn( ) 函数)。 注意成员函数 GetContent( ),这是一个返回常指针的函数,而不是常成员函数。
11.5 指向类成员的指针 指向类成员的指针也叫成员指针,它是用来存放类中成员的地址的。说明成员指针的一般形式为: type class_name :: *pointer; 例: char String :: *cPtr; 成员指针不是类中的成员,因此它必须在类外进行说明。另外,成员指针是一个受一定限制的普通指针,它仅能指向指定类对象中类型与其相同的公有成员。例如,设 String 类中的 pStr 成员被说明成公有的,则可以用以下方式使用 cPtr: String Str("A String"); cPtr = &Str.pStr; cout << cPtr << endl;
第12章 运算符重载 12.1 运算符重载 运算符重载可能是 C++ 语言中最有趣的内容之一。所谓运算符重载,就是在类中说明的一种具有特殊格式的非静态成员函数。 12.1.1 重载运算符 type class_name :: operator @ (<arg_list>) { func_body; } 其中,@ 为除以下 4 个之外的所有合法运算符: :: . *(递引用运算符)?:
// 在类 String( STRCLS.H )中添加两个公有的成员函数: String operator + (String&); String& operator = (Strint&); // 在 STRCLS.CPP 中添加这两个函数的定义: String String :: operator + (String& rs) { String temp; temp.nLen = nLen + rs.nLen; temp.pStr = new char[nLen + 1]; strcpy(temp.pStr, pStr); strcat(temp.pStr, rs.pStr); return temp; }
String& String :: operator = (String& rs) { delete []pStr; nLen = rs.nLen; pStr = new char[nLen + 1]; strcpy(pStr, rs.pStr); return *this; } 说明:赋值运算符重载完全可以定义成一个 void 型函数,所以定义成一个返回同类对象或引用的函数是为了使重载的赋值运算符保留预定义的赋值运算符的性质,即“串连”赋值功能,比如: s3 = s2 = s1;
12.1.2使用运算符重载 String s1("The C++"), s2("language"), s3; s1 = s1 + " "; // 调用串类中的两个运算符重载 s3 = s1 + s2; cout << s3.GetContent() << endl; // 也可以用 s3.Show(); // 输出为:The C++ language 当编译器遇到类似 s1 + s2 这样的表达式时,就将它解释为: s1.operator + (s2) 即调用类中重载的加法运算符。而当遇到类似s1 = s2 这样的表达式时,就将它解释为: s1.operator = (s2) 即调用类中重载的赋值运算符。