zoukankan      html  css  js  c++  java
  • C++解析四-友员函数、内联函数、静态成员

    友元函数

    类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。
    友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。
    如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字 friend,如下所示:

    class Box
    {
       double width;
    public:
       double length;
       friend void printWidth( Box box );
       void setWidth( double wid );
    };

    声明类 ClassTwo 的所有成员函数作为类 ClassOne 的友元,需要在类 ClassOne 的定义中放置如下声明:
    friend class ClassTwo;
    请看下面的程序:

    #include <iostream>
    using namespace std;
    class Box
    {
       double width;
    public:
       friend void printWidth(Box box);
       void setWidth(double wid);
    };
    
    // 成员函数定义
    void Box::setWidth(double wid)
    {
        width = wid;
    }
    
    // 请注意:printWidth() 不是任何类的成员函数
    void printWidth(Box box)
    {
       /* 因为 printWidth() 是 Box 的友元,它可以直接访问该类的任何成员 */
       cout << "Width of box : " << box.width <<endl;
    }
     
    // 程序的主函数
    int main()
    {
       Box box;
       // 使用成员函数设置宽度
       box.setWidth(10.0);
       // 使用友元函数输出宽度
       printWidth(box);
       return 0;
    }

    编译输出:

    kevin@kevin-virtual-machine:~/Documents/Test/C++/four$ ./friend_func 
    Width of box : 10

    友元函数的使用

    因为友元函数没有this指针,则参数要有三种情况:
    1. 要访问非static成员时,需要对象做参数;
    2. 要访问static成员或全局变量时,则不需要对象做参数;
    3. 如果做参数的对象是全局对象,则不需要对象做参数,可以直接调用友元函数,不需要通过对象或指针

    实例代码:

    class INTEGER
    {
        friend void Print(const INTEGER& obj);//声明友元函数
    };
    void Print(const INTEGER& obj)
    {
        //函数体
    }
    void main()
    {
        INTEGER obj;
        Print(obj);//直接调用
    }

     内联函数

    C++ 内联函数是通常与类一起使用。如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。
    对内联函数进行任何修改,都需要重新编译函数的所有客户端,因为编译器需要重新更换一次所有的代码,否则将会继续使用旧的函数。
    如果想把一个函数定义为内联函数,则需要在函数名前面放置关键字 inline,在调用函数之前需要对函数进行定义。如果已定义的函数多于一行,编译器会忽略 inline 限定符。
    在类定义中定义的函数都是内联函数,即使没有使用 inline 说明符。
    下面是一个实例,使用内联函数来返回两个数中的最大值:

    #include <iostream>
    using namespace std;
    inline int Max(int x, int y)
    {
       return (x > y)? x : y;
    }
    
    // 程序的主函数
    int main( )
    {
       cout << "Max (20,10): " << Max(20,10) << endl;
       cout << "Max (0,200): " << Max(0,200) << endl;
       cout << "Max (100,1010): " << Max(100,1010) << endl;
       return 0;
    }

    编译输出:

    kevin@kevin-virtual-machine:~/Documents/Test/C++/four$ ./inline 
    Max (20,10): 20
    Max (0,200): 200
    Max (100,1010): 1010

    内联函数inline:引入内联函数的目的是为了解决程序中函数调用的效率问题,这么说吧,程序在编译器编译的时候,编译器将程序中出现的内联函数的调用表达式用内联函数的函数体进行替换,而对于其他的函数,都是在运行时候才被替代。这其实就是个空间代价换时间的过程。所以内联函数一般都是1-5行的小函数。在使用内联函数时要留神:
    1. 在内联函数内不允许使用循环语句和开关语句;
    2. 内联函数的定义必须出现在内联函数第一次调用之前;
    3. 类结构中所在的类说明内部定义的函数是内联函数。

    内联函数:
    Tip:只有当函数只有10行甚至更少时才将其定义为内联函数。
    定义: 当函数被声明为内联函数之后,编译器会将其内联展开,而不是按通常的函数调用机制进行调用。
    优点: 当函数体比较小的时候,内联该函数可以令目标代码更加高效。对于存取函数以及其它函数体比较短,性能关键的函数,鼓励使用内联。
    缺点: 滥用内联将导致程序变慢。内联可能使目标代码量或增或减,这取决于内联函数的大小。内联非常短小的存取函数通常会减少代码大小,但内联一个相当大的函数将戏剧性的增加代码大小。现代处理器由于更好的利用了指令缓存,小巧的代码往往执行更快。
    结论: 一个较为合理的经验准则是,不要内联超过10行的函数。谨慎对待析构函数,析构函数往往比其表面看起来要更长,因为有隐含的成员和基类析构函数被调用!
    另一个实用的经验准则:内联那些包含循环或 switch 语句的函数常常是得不偿失(除非在大多数情况下,这些循环或 switch 语句从不被执行)。
    有些函数即使声明为内联的也不一定会被编译器内联,这点很重要;比如虚函数和递归函数就不会被正常内联。通常,递归函数不应该声明成内联函数。(递归调用堆栈的展开并不像循环那么简单,比如递归层数在编译时可能是未知的,大多数编译器都不支持内联递归函数)。虚函数内联的主要原因则是想把它的函数体放在类定义内,为了图个方便,抑或是当作文档描述其行为,比如精短的存取函数。

    类的静态成员

    我们可以使用 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;
    }

    编译输出:

    kevin@kevin-virtual-machine:~/Documents/Test/C++/four$ ./static_func 
    Constructor called.
    Constructor called.
    Total objects: 2

    静态成员函数

    如果把函数成员声明为静态的,就可以把函数与类的任何特定对象独立开来。静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符 :: 就可以访问。
    静态成员函数只能访问静态成员数据、其他静态成员函数和类外部的其他函数。
    静态成员函数有一个类范围,他们不能访问类的 this 指针。您可以使用静态成员函数来判断类的某些对象是否已被创建。
    静态成员函数与普通成员函数的区别:
    静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。
    普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针。
    下面的实例有助于更好地理解静态成员函数的概念:

    #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;
          }
          static int getCount()
          {
             return objectCount;
          }
       private:
          double length;     // 长度
          double breadth;    // 宽度
          double height;     // 高度
    };
     
    // 初始化类 Box 的静态成员
    int Box::objectCount = 0;
     
    int main(void)
    {
       // 在创建对象之前输出对象的总数
       cout << "Inital Stage Count: " << Box::getCount() << endl;
       Box Box1(3.3, 1.2, 1.5);    // 声明 box1
       Box Box2(8.5, 6.0, 2.0);    // 声明 box2
       // 在创建对象之后输出对象的总数
       cout << "Final Stage Count: " << Box::getCount() << endl;
       return 0;
    }

    编译输出:

    kevin@kevin-virtual-machine:~/Documents/Test/C++/four$ ./static_func2
    Inital Stage Count: 0
    Constructor called.
    Constructor called.
    Final Stage Count: 2

    静态成员变量在类中仅仅是声明,没有定义,所以要在类的外面定义,实际上是给静态成员变量分配内存。如果不加定义就会报错,初始化是赋一个初始值,而定义是分配内存。

    #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 Box::objectCount;
    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;
    }

    编译输出:

    kevin@kevin-virtual-machine:~/Documents/Test/C++/four$ ./static_func3
    Constructor called.
    Constructor called.
    Total objects: 2

    可以使用静态成员变量清楚了解构造与析构函数的调用情况。

    #include <iostream>
    using namespace std;
    
    class Cpoint{
        public:
            static int value;
            static int num;
        Cpoint(int x,int y){
            xp=x;yp=y;
            value++;
            cout << "调用构造:" << value << endl;
        }
    
        ~Cpoint(){num++; cout << "调用析构:" << num << endl;}
    
        private:
        int xp,yp;
    };
    
    int Cpoint::value=0;
    int Cpoint::num=0;
    class CRect{
        public:
            CRect(int x1,int x2):mpt1(x1,x2),mpt2(x1,x2) {cout << "调用构造
    ";}
            ~CRect(){cout << "调用析构
    ";}
        private:
            Cpoint mpt1,mpt2;
    };
    
    int main()
    {
        CRect p(10,20);
        cout << "Hello, world!" << endl;
        return 0;
    }

    编译输出:

    kevin@kevin-virtual-machine:~/Documents/Test/C++/four$ ./static_func4
    调用构造:1
    调用构造:2
    调用构造
    Hello, world!
    调用析构
    调用析构:1
    调用析构:2
  • 相关阅读:
    Replay_InsertTrigger
    什么是内网IP地址,常用的内网IP地址段有哪些?
    Repeater嵌套绑定Repeater
    window.history.go(1)和window.location.go(1)的区别
    如何对SQL Server中的tempdb“减肥”
    IIS Temporary ASP.NET Files拒绝访问解决方案
    Server Application Unavailable出现的原因及解决方案集锦
    Sqlserver查找非数字数据行
    C++中的指针,指针函数和函数指针
    char * p = "abc"与const char *p = "abc"
  • 原文地址:https://www.cnblogs.com/debruyne/p/9473725.html
Copyright © 2011-2022 走看看