深入探究C++ 类成员(Class Members)

一、定义

  在class的声明里头,真正有用的两样东西是data members 和 member functions:

  Data members:表示根据这个class所产生的object里头会有些什么东西,它事实上也是占据object内存的唯一东西(除非引入虚拟机制)。通常为数据的封装性,我们把data members声明为private或protected。

  Member functions:是用来处理data members的函数。通常为了界面的开放性,我们把member functions设计为pubilc。

二、Data Members(数据成员)

  Data members的声明和一般non-class的变量声明形式一样,以CPoint为例:

 1 class CPoint
 2 {
 3 public:
 4     CPoint(float x=0.0):_x(x){}
 5     
 6     float x(){return _x;}
 7     void x(float xval){_x=xval;}
 8     
 9 protected:
10     float _x;     //data member
11     
12 };

  第10行的_x就是一个data members。

  如果data member本身不是一个变量,而是一个class object,这种情况比较特殊,称为composition(组合),而这种object被称为embedded objects或object member。Composition被用来描述has a的关系(例如汽车有一个引擎),inheritance(继承)用来描述is a kind of的关系(例如汽车是一种交通工具)。稍后我们会分别描述composition和inheritance两种情况。

三、Member Functions(成员函数)

  Member functions用来处理class data members。在数据封装的情况下,member functions可以说就是一个class(或object)的对外界面。这些functions并不占用object的内存空间,它被编译器处理过后,以完全等同一般的global functions的身份出现,独立于任何object之外。同一份函数代码如何能够处理依据同一个class产生出来的不同object的data members呢?关键在于有一个this指针隐藏在member functions的参数之中,也隐藏在member functions函数代码对于data members的处理动作上,是编译器自动为我们加上去的。

 四、如何取用 Class Members

  要取得class members,如果是在class scope之内,直接使用其名称即可。但如果是在class scope之外,你有三种方式可以办到:

  1.透过dot operator(.),例如:

1 CPoint aPoint; // 产生一個 CPoint object,名为 aPoint
2 aPoint.x(); // 唤 起 CPoint::x()
3 aPoint._x = 7.0; // 使 用 CPoint 的 data member

  2.透过arrow operator(->),例如:

1 CPoint* pPoint = new CPoint; // 产生一个 CPoint object,由 pPoint 指向它
2 pPoint->x(); // 唤 起 CPoint::x()
3 pPoint->_x = 7.0; // 使用 CPoint 的 data member

  3.透过scope resolution operator(::),例如:

1 CPoint::foo(); // foo() must be a static member function
2 CPoint::ratio = 0.2; // ratio must be a static data member

  第三种方法未通过任何对象访问成员,表明这些成员应属于类层次,即静态成员(Static Members)。若错误地通过对象访问非静态成员,如CPoint::foo(),将引发错误。静态成员应通过类名访问,非静态成员应通过对象实例访问。

五、特殊的 Static Members(静态成员)

  Class中可以使用static关键,使members成为静态。

  静态的意思是这份members属于class而非object所有。听起来有点奇怪,因为class只是属性,而object才是实体。很难一言以敝之,让我们将members细分为data 和function来讨论。

  当你看完以下的剖析,你会认为static data members和global variables很像,static member functions和global functions 很像。一点没错,从功能而言,global 可以取代static。不过,以面对对象的观点来看,这些members如查在性质上与class息息相关,不是设计成class的static members为佳,面对对象的程序设计中,我们希望global的东西越少越好。

1. Static Data Members(静态数据成员)

  虽说data属于object实体所有,但程序设计中有些数据也希望由各object共享,所以提升到class层级比较理想。例如我们设计一个银行储户的class:

1 class SavingAccount
2 {
3 private:
4     char   m_name[40];//储户姓名
5     char   m_addr[60];//储户地址
6     double m_total;//账户金额
7     double m_rate;//利率
8 };

  这家银行采用浮动利率,每个账户的利息是根据当天的利率来计算的,这时m_rate就不适合成为每个object的数据了,否则每天一开市,光把所有的账户内容调出来,修改m_rate的值,就花了不少时间。m_rate应该独立于各object之外,成为class的独一无二的数据;具体怎么做?在m_rate前面加上Static关键字就行了:

1 class SavingAccount
2 {
3 private:
4     char   m_name[40];//储户姓名
5     char   m_addr[60];//储户地址
6     double m_total;//账户金额
7     Static double m_rate;//利率(注意,这里只是声明,不是定义,未分配内存)
8 };

  Static data members不属于object的一部份,而是class的一部份,所以程序可以在还没有生成任何object的时候就处理Static data members。但首先,你必须定义它(为它分配内存)。

你应该在.CPP文件中且在class以外的任何区域设定其初始值,例如在main()之中,或在global function中,或任何函数之外:

1 double SavingAccount::m_rate = 0.0075; // 定义并设定初值
2 void main() // 不指定初值也可以
3 {
4 ...
5 }

  请注意用词上的严谨:上述“定义”动作常被误以为是“初始化”动作。如果只是单纯的初值设定动作,应该可以安排在class constructor中,但上一行绝对无法如此。此外,你也不应该把上述的定义安排于.H文件中,因为这么一来它可能会被含入许多.CPP文件中,于是就重复定义了(会发生连接错误)。

  上述动作有没有考虑m_rate是private数据呢?没有关系,定义static data members,不受任何封装层级的约束。请注意上述动作也指出static data members的类型,因为这是一个定义动作,不是一个数值指定动作,如果没有做这个动作,m_rate就没有获得内存,会发生异常错误:

error LNK2001: unresolved external symbol "private: static double
SavingAccount::m_rate"(?m_rate@SavingAccount@@2HA)

  下面是存取static data members的一种方式,注意:此时还没有任何object存在:

1 void main()
2 {
3 SavingAccount::m_rate = 0.0072; // 此行要成立,m_rate 需改为public
4 }

   第二种方式是产生一个object后,透过object来处理static data members:

1 void main()
2 {
3 SavingAccount myAccount;
4 myAccount.m_rate = 0.0072; // 此行要成立,m_rate 需改为 public
5 }

  请注意,static data members并不是因为myAccount object的实现而才得以实现,它本来就存在。因此第一种的处理方式不容易给人错误印象;

2. Static Menber Functions(静态成员函数)

  既然static data members 是超越于object之外存在的,一般的member functions能否处理它呢?要知道,,member function得经由某个object才能调用,该object地址则成为所谓的this指针,但由于static data members并不需要靠this指针的指引来决定其地址,所以其实任何函数(包含global函数),只要access level允许,都可以处理static data members。

  但member function得经由某个object才能调用,如果你在尚未产生任何object之前,就希望透过某个member function来更我以为前例的static m_rate,而它又是private(以至于不能被global函数处理)那么一定要写个static member function了:

 1 class SavingAccount
 2 {
 3 private:
 4     char   m_name[40];//储户姓名
 5     char   m_addr[60];//储户地址
 6     double m_total;//账户金额
 7     Static double m_rate;//利率(注意,这里只是声明,未分配内存)
 8 public:
 9     static void setRate(double newRate){m_rate=newRate;}
10 };
11 
12 double SavingAccount::m_rate=0.0072; //设初值
13 
14 int main()
15 {
16     SavingAccount myAccount;
17     //可以透过object调用static member functions
18     myAccount.setRate(0.0072);
19     //也可以直接调用static memberfunction
20     SavingAccount::setRate(0.0072);
21     
22     return 0;
23 }

  由于static member functions 不需要任何object的存就可以被调用,所以编译器不会为它暗加一个this指针。也因为如此,static member function无法处理任何non-static data members,因为member function之所以能够以单一一份函数代码处理各个object的数据而不乱,完全靠的是this指针。

3. Static Object Members(静态对象成员)

  Object members应该被视为和一般的data members 样地位,只不过它是class类型,而一般data members是内建类型。当它冠上static,表示它所寄生的class一旦声明完成(未产生object),并且对static members做了初始化动作,这个static object member就已经在内存中有了实体。例如:

  

 1 class Point3d
 2 { public:
 3 // ...
 4 public: // (protected: is better)
 5 static Point3d origin;
 6 float x, y, z;
 7 };
 8 Point3d Point3d::origin; // 別忘了这个动作(其实就是实体配置)
 9 
10 int main()
11 {
12     printf("&Point3d::x = %p\n", &Point3d::x); // 00000004
13     printf("&Point3d::y = %p\n", &Point3d::y); // 00000008
14     printf("&Point3d::z = %p\n", &Point3d::z); // 0000000C
15     printf("&Point3d::origin = %p\n", &Point3d::origin); // 0040A450
16     printf("&Point3d::origin.x = %p\n", &Point3d::origin.x); // 0040A454
17     printf("&Point3d::origin.y = %p\n", &Point3d::origin.y); // 0040A458
18     printf("&Point3d::origin.z = %p\n", &Point3d::origin.z); // 0040A45C
19     Point3d pt3d;
20     cout << "&pt3d.x = " << &pt3d.x << endl;
21     cout << "&pt3d.y = " << &pt3d.y << endl;
22     // 0x0063FDEC
23     // 0x0063FDF0
24     cout << "&pt3d.z = " << &pt3d.z << endl; // 0x0063FDF4
25     
26 }

4. Static Objects(静态对象)

  static 关键字也可以应用在object身上,像这样:

static CPoint aPoint(3.6);

  aPoint于是就成为一个static object,这个object的生命将持续到整个程序结束为止。

 

热门相关:亡国公主穿成王府寡妇:二嫁王妃   嫡女谋:逆天三小姐   学霸你女朋友掉了   美食萌后:皇上,喂不饱!   娇妻太甜:禁欲总裁,别太急