美文网首页我爱编程C++
C++第六弹---静态成员与友元

C++第六弹---静态成员与友元

作者: 黄巴巴 | 来源:发表于2018-06-29 22:16 被阅读2次

    静态成员

    静态成员的定义或声明要加个关键static。静态成员可以通过双冒号来使用即<类名>::<静态成员名>。

    我们可以使用 static 关键字来把类成员定义为静态的。当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本。

    静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。我们不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化,如下面的实例所示。
    实例:

    #include <iostream>
     
    using namespace std;
     
    class Box
    {
       public:
          static int objectCount;
          // 构造函数定义
          Box(double l=2.0, double b=2.0, double h=2.0)
          {
             cout <<"Constructor called." << endl;
             length = l;
             breadth = b;
             height = h;
             // 每次创建对象时增加 1
             objectCount++;
          }
          double Volume()
          {
             return length * breadth * height;
          }
       private:
          double length;     // 长度
          double breadth;    // 宽度
          double height;     // 高度
    };
     
    // 初始化类 Box 的静态成员
    int Box::objectCount = 0;
     
    int main(void)
    {
       Box Box1(3.3, 1.2, 1.5);    // 声明 box1
       Box Box2(8.5, 6.0, 2.0);    // 声明 box2
     
       // 输出对象的总数
       cout << "Total objects: " << Box::objectCount << endl;
     
       return 0;
    }
    

    当上面的代码被编译和执行时,它会产生下列结果:

    Constructor called.
    Constructor called.
    Total objects: 2

    1. 静态数据成员
    • 静态数据成员定义
      静态数据成员实际上是类域中的全局变量。所以,静态数据成员的定义(初始化)不应该被放在头文件中。 其定义方式与全局变量相同。举例如下:
      test.h文件 :
    class   base
    {   
           private:   
           static   const   int   _i;
          //声明,标准c++支持有序类型在类体中初始化,但vc6不支持。   
    };  
    

    test.cpp文件:

    const   int   base::_i=10;//定义(初始化)时不受private和protected访问限制. 
    

    注:不要试图在头文件中定义(初始化)静态数据成员。在大多数的情况下,这样做会引起重复定义这样的错误。即使加上#ifndef #define #endif或者#pragma once也不行。

    • 静态数据成员被 类 的所有对象所共享,包括该类派生类的对象。即派生类对象与基类对象共享基类的静态数据成员。举例如下:
    class   base{   
                  public   :   
                  static   int   _num;//声明   
    };   
            int   base::_num=0;//静态数据成员的真正定义   
            class   derived:public   base{   
    };   
    int main()   
    {   
         base   a;   
         derived   b;   
         a._num++;   
         cout<<"base   class   static   data  number   _num   is"<<a._num<<endl;   
         b._num++;   
         cout<<"derived  class  static   data  number  _num   is"<<b._num<<endl;   
    }   
    //   结果为1,2;可见派生类与基类共用一个静态数据成员。 
    
    • 静态数据成员可以成为成员函数的可选参数,而普通数据成员则不可以。举例如下:
    class   base{   
              public   :   
              static   int   _staticVar;   
              int   _var;   
              void   foo1(int   i=_staticVar);//正确,_staticVar为静态数据成员   
              void   foo2(int   i=_var);//错误,_var为普通数据成员   
          }; 
    
    • 静态数据成员的类型可以是所属类的类型,而普通数据成员则不可以。普通数据成员的只能声明为所属类类型的指针或引用。举例如下:
    class   base{   
              public   :   
              static   base   _object1;//正确,静态数据成员   
              base   _object2;//错误   
              base   *pObject;//正确,指针   
              base   &mObject;//正确,引用   
          };
    
    • 静态数据成员的值在const成员函数中可以被合法的改变。举例如下:
    class   base{   
              public:   
              base(){_i=0;_val=0;}   
        
              mutable   int   _i;   
              static   int   _staticVal;     
              int   _val;   
              void   test()   const{//const   成员函数   
        
                    _i++;//正确,mutable数据成员   
                    _staticVal++;//正确,static数据成员   
                    _val++;//错误   
        
              }   
          };   
          int   base::_staticVal=0;  
    
    • 公共静态数据成员可被类的外部访问,保护或私有静态数据成员只可被类的内部访问。

    静态数据成员经常被用于以下几种场合:
    (1)用来保存流动变化对象的个数;
    (2)作为一个标志,指示一个特定的动作是否发生;
    (3)一个指向链表第一成员或最后一个成员的指针。

    1. 静态成员函数
    • 静态成员函数的地址可用普通函数指针储存,而普通成员函数地址需要用类成员函数指针来储存。举例如下:
    class   base{   
                  static   int   func1();   
                  int   func2();   
              };   
        
              int   (*pf1)()=&base::func1;//普通的函数指针   
              int   (base::*pf2)()=&base::func2;//成员函数指针  
    
    • 静态成员函数不可以调用类的非静态成员。因为静态成员函数不含this指针。
    • 静态成员函数不可以同时声明为 virtual、const、volatile函数。举例如下:
    class   base{   
                  virtual   static   void   func1();//错误   
                  static   void   func2()   const;//错误   
                  static   void   func3()   volatile;//错误   
            };  
    

    最后要说的一点是,静态成员是可以独立访问的,也就是说,无须创建任何对象实例就可以访问。

    友元

    采用类的机制后实现了数据的隐藏与封装,类的数据成员一般定义为私有成员,成员函数一般定义为公有的,依此提供类与外界间的通信接口。但是,有时需要定义一些函数,这些函数不是类的一部分,但又需要频繁地访问类的数据成员,这时可以将这些函数定义为该函数的友元函数。除了友元函数外,还有友元类,两者统称为友元。友元的作用是提高了程序的运行效率(即减少了类型检查和安全性检查等都需要时间开销),但它破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员。

    1. 友元函数
      友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数,它不属于任何类,但需要在类的定义中加以声明,声明时只需在友元的名称前加上关键字friend,其格式如下:
      friend 类型 函数名(形式参数);

      友元函数的声明可以放在类的私有部分,也可以放在公有部分,它们是没有区别的,都说明是该类的一个友元函数。一个函数可以是多个类的友元函数,只需要在各个类中分别声明。友元函数的调用与一般函数的调用方式和原理一致。

    2. 友元类
      友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息(包括私有成员和保护成员)。
      当希望一个类可以存取另一个类的私有成员时,可以将该类声明为另一类的友元类。定义友元类的语句格式如下:
      friend class 类名;
      其中:friend和class是关键字,类名必须是程序中的一个已定义过的类。

      例如,以下语句说明类B是类A的友元类:

           class A
           {
                  …
           public:
                  friend class B;
                  …
           };
    

    经过以上说明后,类B的所有成员函数都是类A的友元函数,能存取类A的私有成员和保护成员。

    注意:
    (1) 友元关系不能被继承。
    (2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。
    (3) 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的声明

    友元示例:

    #include <iostream> 
    using namespace std; 
    class Internet 
    { 
    public: 
    Internet(char *name,char *address) // 改为:internet(const char *name , const char *address)
    { 
    strcpy(Internet::name,name); 
    strcpy(Internet::address,address); 
    } 
    friend void ShowN(Internet &obj);   //友元函数的声明 
    public:              // 改为:private
    char name[20]; 
    char address[20]; 
    }; 
    void ShowN(Internet &obj)        //函数定义,不能写成,void Internet::ShowN(Internet &obj) 
    { 
    cout<<obj.name<<endl;          //可访问internet类中的成员
    } 
    void main() 
    { 
    Internet a("中国软件开发实验室","www.cndev-lab.com"); 
    ShowN(a); 
    cin.get(); 
    } 
    

    上面的代码通过友元函数的定义,我们成功的访问到了a对象的保护成员name,友元函数并不能看做是类的成员函数,它只是个被声明为类友元的普通函数,所以在类外部函数的定义部分不能够写成void Internet::ShowN(Internet &obj),这一点要注意。

    相关文章

      网友评论

        本文标题:C++第六弹---静态成员与友元

        本文链接:https://www.haomeiwen.com/subject/artzyftx.html