代码改变世界

学习C++ -> 类的特殊数据成员

2013-02-23 22:51  wid  阅读(8450)  评论(4编辑  收藏  举报

学习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++ -> 复制构造函数