学习C++ -> 类的特殊数据成员
在构造函数一节的介绍中, 我们已经提到了在C++中有几类特殊的数据成员不能使用构造函数进行初始化, 他们有自己的初始化方式, 下面就具体介绍下这几种数据成员。
一、const 数据成员
const 类型的数据成员具有只读属性, 在构造函数内进行初始化是不允许的, 例如以下代码:
1 #include <iostream> 2 3 using namespace std; 4 5 class A 6 { 7 public: 8 A(int n = 0) { num = n; } //在构造函数内使用 n 初始化const型数据成员 num 9 void showNum() { cout<<"num = "<<num<<endl; } 10 private: 11 const int num; //const 型数据成员 12 }; 13 14 int main() 15 { 16 A a(10); 17 a.showNum(); 18 19 return 0; 20 }
尝试编译运行时报错:
error: uninitialized member 'A::num' with 'const' type 'const int' error: assignment of read-only data-member 'A::num'
要初始化 const 型的数据成员, 必须通过初始化表达式来进行初始化, 一经初始化, 其值确定, 不能再被修改, 通过初始化表达式形式如下:
1 #include <iostream> 2 3 using namespace std; 4 5 class A 6 { 7 public: 8 A(int n = 0):num(n) { } //使用初始化表达式对const型数据成员进行初始化 9 void showNum() { cout<<"num = "<<num<<endl; } 10 private: 11 const int num; 12 }; 13 14 int main() 15 { 16 A a(10); 17 a.showNum(); 18 19 return 0; 20 }
二、引用类型的成员
对于引用类型的成员, 同样只能通过初始化表达式进行初始化, 错误的示例这里不再举出, 一个通过初始化表对引用类型初始化的示例:
1 #include <iostream> 2 3 using namespace std; 4 5 class A 6 { 7 public: 8 A(int &rn):rNum(rn) { } //使用初始化表达式对引用型数据成员进行初始化 9 void showNum() { cout<<"num = "<<rNum<<endl; } 10 private: 11 int &rNum; //引用类型 12 }; 13 14 int main() 15 { 16 int x = 10, &rx = x; 17 A a(rx); //传递引用类型 18 a.showNum(); 19 20 return 0; 21 }
三、类对象成员
类的对象可以作为另一个类的数据成员, 在定义类时, 与普通的数据成员一样, 在声明时无法进行初始化, 例如有个Line类中有两个 Point 类的对象作为数据成员, 在声明时
private: int xPos(10, 20); int yPos(100, 200);
这样是错误的, 所以在初始化类对象成员时也需要一定的方法。
情况一:
还以 Line 类和 Point 类为例, 当 Point 类中的数据成员全部为 public 时, 可以在 Line 类的构造函数内完成初始化, 像以下示例的情况:
1 #include <iostream> 2 3 using namespace std; 4 5 class Point 6 { 7 public: 8 void printPoint() { cout<<"("<<xPos<<", "<<yPos<<")\n"; } 9 int xPos; //public类型的 xPos 和 yPos 10 int yPos; 11 }; 12 13 class Line 14 { 15 public: 16 Line(int x1, int y1, int x2, int y2) 17 { 18 M.xPos = x1; M.yPos = y1; //在构造函数内完成类对象的初始化 19 N.xPos = x2; N.yPos = y2; 20 } 21 void printLine() { M.printPoint(); N.printPoint(); } 22 private: 23 Point M; //类对象成员, M和N 24 Point N; 25 }; 26 27 int main() 28 { 29 Line L(10, 20, 100, 200); 30 L.printLine(); 31 32 return 0; 33 }
编译运行的结果:
(10, 20) (100, 200) Process returned 0 (0x0) execution time : 0.500 s Press any key to continue.
代码说明:
由于 Point 类中的数据成员全为 public 的, 所以可以通过 对象名.数据成员名 的方式直接进行访问。 可以看到, Line 类中有两个 Point 类型的数据成员, M和N, 然后通过 对象名.数据成员名 的方式在 Line 类的构造函数中完成初始化。
情况二:
由于类的数据成员一般都为 private 型的, 再使用上面的方式进行初始化就不适合了, 因为 private 数据是不允许外部直接访问的, 将 Point 类中的数据成员改为 private 后再编译运行代码便会报出 error: 'int Point::xPos' is private 这样的错误。
要在 Line 类中对 Point 的 private 数据成员进行正常的初始化同样需要借助初始化表达式来完成, 修改后的示例:
1 #include <iostream> 2 3 using namespace std; 4 5 class Point 6 { 7 public: 8 Point(int x, int y) { xPos = x; yPos = y; } 9 void printPoint() { cout<<"("<<xPos<<", "<<yPos<<")\n"; } 10 private: 11 int xPos; //private类型的 xPos 和 yPos 12 int yPos; 13 }; 14 15 class Line 16 { 17 public: 18 Line(int x1, int y1, int x2, int y2):M(10, 20), N(100, 200) { ; } //借助初始化表达式完成对M,N的初始化 19 void printLine() { M.printPoint(); N.printPoint(); } 20 private: 21 Point M; //类对象成员, M和N 22 Point N; 23 }; 24 25 int main() 26 { 27 Line L(10, 20, 100, 200); 28 L.printLine(); 29 30 return 0; 31 }
运行输出后的结果与上例中是相同的。
代码说明:
通过初始化表达式对象 L 中的 M、N 数据成员的初始化顺序如下: 首先 M 对象的构造函数被调用, 接着调用 N 对象的构造函数, 最后 Line 类的构造函数被调用, 这样便完成了类对象成员的初始化。
四、static类型数据成员
static 类型的数据成员为静态成员, 他的特点是: 无论对象创建了多少个, 该数据成员的实例只有一个, 会被该类所创建的所有对象共享, 其中任何一个对象对其操作都会影响到其他对象。该类型的数据初始化是放在类外进行的, 其基本格式如下:
类型 类名::成员变量名 = 初始化值/表达式;
一个示例: 统计一共创建了多少个对象, 并且在一番销毁后还剩多少。
1 #include <iostream> 2 3 using namespace std; 4 5 class Book 6 { 7 public: 8 Book() { ibookNumber++; } //通过构造函数访问static型数据ibookNumber并使其自增1 9 ~Book() { ibookNumber--; } //对象在被撤销时将static型数据ibookNumber并使其自减1 10 void showNum() { cout<<"Book number = "<<ibookNumber<<endl; } 11 private: 12 static int ibookNumber; //static类型数据成员 ibookNumber 13 }; 14 15 int Book::ibookNumber = 0; //在类外对static类型的数据成员进行初始化 16 17 int main() 18 { 19 Book A; Book B; Book C; Book D; Book E; //创建一些对象 20 A.showNum(); //使用对象A查看当前对象个数 21 B.showNum(); //使用对象B查看当前对象个数 22 cout<<"销毁一些对象...\n"; 23 B.~Book(); C.~Book(); //将B、C对象进行撤销 24 D.showNum(); //使用D对象查看剩余对象个数 25 E.showNum(); //使用E对象查看剩余对象个数 26 27 return 0; 28 }
编译运行的结果:
Book number = 5 Book number = 5 销毁一些对象... Book number = 3 Book number = 3 Process returned 0 (0x0) execution time : 0.266 s Press any key to continue.
代码说明:
在上面代码的类Book中, 我们声明了一个 static 类型的 ibookNumber 作为创建多少对象的统计变量, Book的构造函数的作用是当对象创建时将 ibookNumber 的值自增1, 而析构函数的则负责将 ibookNumber 自减1。
main函数中, 创建了 A-E共5个对象, 在创建完成后, 通过 A 对象输出对象的总数和 B 对象输出的结果是相同的, 都是5个, 然后销毁 B、C 对象, 用D、E对象查看剩余对象个数, 结果都为3, 可以看出, static型的数据成员任何对象都可以进行访问, 并且在创建后所产生的实例是唯一的。
--------------------
wid, 2013.02.23
上一篇: 学习C++ -> 复制构造函数