zoukankan      html  css  js  c++  java
  • c++学习笔记

     

    1         函数

    1.1      内联函数

    inline bool isNumber(char c)

    {

        return (c=='1');

    }

    int main(int argc, _TCHAR* argv[])

    {

        //cout<<" Hello World! \n";

        //printf("Hello World!");

        if (isNumber('1'))

           printf("It is a Number");

        else

           printf("It isn't a Number");

        return 0;

    }

    这种用inline定义的函数就是内联函数。

    内联函数的出现是为了解决效率问题;

    内联函数的函数体一般在15行之间;

    内联函数内部不允许有控制语句,如果有控制语句,则它只会被当作一般的函数。

    2         程序结构

    2.1      外部存储类型

    默认的函数声明或定义总是extern的;

    extern的是变量声明,不是变量实现;

    2.2      静态函数

    c++中所谓的静态函数和c#中的是不同的,函数之所以声明为static,是为了和extern不同,就是让该函数只在这个文件内有效。

    2.3      作用域

    主要可以分为以下几种:

    l         局部作用域;

    void fn()

    {

        if (int i== 5) //i的作用域自此开始

           i = 2*i;

        else

           i=100;

        //i的作用域自此结束

        printf(i);    //错误,i没定义

    }

    l         函数作用域;

    标号是唯一具有函数作用域的标识符。所谓的标号就是给goto使用的一个标识符。

    l         文件作用域;

    所谓的文件作用域就是从声明的地方开始指导文件的结尾。

    l         块作用域;

    l         函数原型作用域;

    所谓函数原型就是函数的声明;

    比如:

    void Area(double Width, double Height);

    另外,这里说说函数原型,其实上面的声明和下面的声明是等效的:

    void Area(double, double);

    l         类作用域;

    2.4      可见性

    可见性对于分析两个同名标识符作用域嵌套的特殊情况特别有用。

    2.5      生命期

    2.6      头文件

    具有外部存储类型的声明可以在多个源文件中使用,因此方便的方法是将他们放在头文件中。头文件起着源文件之间接口的作用。

    头文件一般可以包括:

    l         类型声明;

    l         函数声明;

    l         内联函数定义;

    l         常量定义;

    l         数据声明;

    l         枚举;

    l         包含指令;

    l         宏定义;

    但是头文件一般不适合包含:

    l         一般函数定义;

    l         数据定义;

    l         常量聚集定义;

    2.7      多文件结构

    大一些的程序倾向于使用多个文件;

    2.8      编译预处理

    这里解释一下#include的两种格式:

           #include<…>//这种方式适合于用来引用库文件;

    #inlcude “…” // 这种格式适合用来引用工程内用户自己定义的文件

    3         数组

    3.1      数组定义

    void LearnArray()

    {

        char buffer[5];

        ...

    }

    这样就定义了一个数组;

    数组的下标从0开始;

    其他的暂时不做介绍。

     

    在编译的时候,数组的下标必须是确定的,像下面这样的定义是错误的。

    void LearnArray()

    {

        int size = 50;

        int buffer[size];

       

    }

    不过用常数来定义下标则是允许的。

    const int size = 50;

    void LearnArray()

    {

        int Buffer[size];

    }

    3.2      字符数组

    void LearnArray()

    {

        char Buffer[10] = "hello";

    }

    这个数组的内容是:

    Buffer[0]=h, Buffer[0]=e, Buffer[0]=l, Buffer[0]=l, Buffer[0]=o, Buffer[0]=\0

    也就是会自动添加一个空字符到数组的结尾;

    3.3      向函数传递数组

    无论何时,把数组作为参数传递给函数,其实只是把数组的地址传递给函数。

     

           如果要让一个函数求数组元素的和,需要传递一个数组参数和数组大小参数。

    3.4      二维数组

    int Buffer[2][3] = {{1,2,3},{4,5,6}};

    也可以省略一维大小,上面的声明等效于:

    int Buffer[][3] = {1,2,3,4,5,6};

    3.4.1      作为参数传递

    作为参数传递一个二维数组给一个函数,其意义也为内存地址;

    传递的时候也是既要传递地址也要传递大小,而且是多维的大小;

    3.4.2      降维处理

    因为数组在内存中是线性排列的,所以可以用一个单循环来遍历。

    void TestMax()

    {

        int ASerialNumber[2][3] = {{3,4,8},{1,2,9}};

        printf(Maxnum(@ASerialNumber[0][0],2*3));//传递第一个元素的地址和数组个数

    }

    void Maxnum(int Grade[], int num)

    {

        int max=0;

        for(int i=0;i<num;i++)

        {

           if(Grade[i]>max;

               max = Grade[i];

        }

        return max;

    }

    4         指针

    指针是有类型的。

    4.1      定义指针变量

    下面这三个定义方式是等效的:

    int * iPtr1;

    int* iPtr2;

    int *iPtr3;

    4.1.1      给指针赋值

    int Counter  = 18;

    iPtr1  = &Counter;

    4.1.2      获取指针指向的地址的内容

    下面这两条输出语句的内容是等效的。

    void LearnPointer()

    {

        iPtr1  = &Counter;

        cout << Counter;

        cout << *iPtr1;  //这样的操作叫做间接引用

    }

    这里要注意区分操作符* 在指针上的两种用途:一种是用来定义指针;一种是用来做间接引用。

     

    4.1.3      指针是有类型的

    所以下面的代码是错误的

    void TestPointerType()

    {

        float AFloat  = 11.1;

        int* iPointer  = &AFloat;//error

    }

    4.2      指针运算

    4.2.1      数组的名字是指针

    所以下面这个例子能够通过编译

    void TeatArrayAndPointer1()

    {

        int Buffer[10];

        int * iPointer = Buffer;//等效于int * iPointer = &Buffer[0];

    }

    什么类型的数组,他的数组名就是什么类型的指针。

    如果像下面这样肯定就不能通过编译。

    void TeatArrayAndPointer2()

    {

        int Buffer[10];

        float * iPointer = Buffer;//error

    }

    4.2.2      指针的运算

    指针是可以运算的,所以下面函数执行之后将输出:

    0

    1

    2

    3

    4

    5

    6

    7

    8

    9

    void TeatArrayAndPointer3()

    {

        int Buffer[10];

        for(int i=0; i<10;i++)

        {

           Buffer[i] = i;

        }

        int * iPointer = Buffer;//等效于int * iPointer = &Buffer[0];

        for(int i =0;i<10;i++)

        {

           cout<< *iPointer<<endl;

           iPointer++;

        }  

    }

    但是我们要注意,只有加法和减法可以用于指针运算;

    4.2.3      指针与数组

    下标操作是针对于地址,而不仅仅是针对于数组名的,所以:

    void TeatArrayAndPointer4()

    {

        int Buffer[10];

        for(int i=0; i<10;i++)

        {

           Buffer[i] = i;

        }

        int * iPointer  = Buffer;

        //下面四条语句等效;

        cout<<iPointer[2]<<endl;

        cout<<Buffer[2]<<endl;

        cout<<*(iPointer+2)<<endl;

        cout<<*(Buffer+2)<<endl;

    }

     

    不过我们要注意的一点是:数组名是指针常量,这要区别于指针变量;

     

    4.3      堆内存分配

    4.3.1      使用古董式的malloc

    void LearnUseAlloc()

    {

        int Size;

        int * iArray;

        cout<<"请输入数组大小:"<<endl;

        cin>>Size;

        cout<<endl;

        iArray  = (int *)malloc(Size * sizeof(int));

        for(int i=0;i<Size;i++)

        {

           iArray[i] = i;

        }

        for(int i=0;i<Size;i++)

        {

           cout<<iArray[i]<<",";

        }

        free(iArray);//释放

    }

     

    注意,分配的内存要释放掉:

     

    4.3.2      使用c++专有的操作符newdelete

    给个例子就明白了

    void LearnUseNewAndDelete()

    {

        int Size;

        int * iArray;

        cout<<"请输入数组大小:"<<endl;

        cin>>Size;

        cout<<endl;

        iArray  = new int[Size];

        for(int i=0;i<Size;i++)

        {

           iArray[i] = i;

        }

        for(int i=0;i<Size;i++)

        {

           cout<<iArray[i]<<",";

        }

        delete[]iArray;  

    }

    4.4      const指针

    这里其实有三种

    4.4.1      指向常量的指针

    定义常量指针,只能限制指针的间接访问操作,而不能规定指针指向的值本身的操作规定性。例如:

    void LearnConstPointer()

    {

        int c  = 10;

        const  int a  = 11;

        const int * iPointer  = &a;

        * iPointer  = 11; //error,常量指针,不能修改指向的量的值

        iPointer  = &c;

        * iPointer  = 11; //error,同上

    }

     

    常量指针的用处,例如下面这段代码,用了常量指针之后,就可以放置StringCopy对源字符串的内容做修改。

    void StringCopy(char * dest, const char * source)

    {

        while(*dest++= *source++);

    }

    4.4.2      指针常量

    和指向常量的指针相反,这里指针本身就是一个常量,也就是指向的地址不能变,但是地址内的内容是可以变的。例子如下:

    void LearnConstPointer2()

    {

        int Number = 10;

        int Number2 = 20;

        int * const ConstPointer  = &Number;

        //ConstPointer  = &Number2;//error

        *ConstPointer  = 11;    

    }

    4.4.3      指向常量的指针常量

    这个就更绝了,是上面两个的综合体。

    void LearnConstPointer3()

    {

        int Number = 10;

        int Number2 = 20;

        const int * const ConstPointer  = &Number;

        //ConstPointer  = &Number2;//error

        //*ConstPointer  = 11;      //error

    }

    4.5      指针与函数

    4.5.1      把指针作为参数

    如果把一个数组传递给一个函数,那么其实传递的是该数组的首地址。

    int MethodAndPointer(int Buffer[], int Count);

    int main(int argc, _TCHAR* argv[])

    {

        int Buffer[5]={1,2,3,4,5};

        cout<<MethodAndPointer(Buffer,sizeof(Buffer)/sizeof(int))<<endl;

        return 0;

    }

     

    int MethodAndPointer(int Buffer[], int Count)

    {

        int Sum=0;

        for(int i=0; i<Count; i++)

        {

           Sum = Sum+ *(Buffer+i);

        }

        return Sum;

    }

    传递的数组参数在MethodAndPointer()中其实是一个指针,所以声明:

    int MethodAndPointer(int * Buffer, int Count)

    int MethodAndPointer(int Buffer[], int Count)

    其实是等效的。

    正是由于Buffer其实是指针而不是数组,所以它所占用的控件是指针变量的大小而不是数组的大小,这就是第二个参数Count必须有的原因。

    4.5.2      指针函数

    返回类型是指针的函数就叫做指针函数。

    但是要注意,返回的地址不能是函数内部具有局部作用域的地址。

    例如下面这种使用方式是存在问题的,但是不会报错。

    int * ReturnPointerType()

    {

            int i = 10;

            return &i;

    }

     

    所以一般返回的是堆地址,当然也可以是全局变量的地址。

    4.5.3      void指针

    void指针又叫做空指针,空指针是没有类型的,所以不能对他进行指针运算,也不能进行间接引用。

    void LearnUseVoidPointer()

    {

        void * voidPointer;

        int i = 10;

        float f  = 10.0;

        int * intPointer = &i;

        float * floatPointer = &f;

        voidPointer = intPointer; //允许

        voidPointer = floatPointer; //允许

    //  floatPointer = voidPointer; //error

        floatPointer = (float*)voidPointer;//允许

    }

    4.6      字符指针

    字符串常量的类型是指向字符的指针,所以下面函数的输出不会是equal,因为两个字符串的地址是不同的。它和在c#中不同。==比较的是指针是否相同。

     

    看字符指针主要是用来看程序的,实际使用的时候尽量使用string类,这个类的用法基本和c#的类差不多。就不要再介绍了,下面给出一个例子;

    void LearnUseString2()

    {

        string s1 = "hello";

        string s2 = "world";

        char * pc = ", ";

        string s3 = s1 + pc + s2;

        cout<<s3.c_str()<<endl;

    }

    这个例子中的用法基本和c#中的用法没有差别了。

    5         引用类型

    5.1      引用的概念

    引用是个别名,当建立引用时,程序用另一个变量或对象的名字初始化它。从那时起,引用作为目标的别名而使用,对引用的改动实际就是对目标的改动。

    引用不是值,不占用内存空间。

    引用在声明的时候就必须被初始化,否则将产生编译错误。

    void LearnReference()

    {

        int Number =10;

        int &intRef = Number;

        intRef = 20;

        cout<<intRef<<endl;

    }

    该函数的输出将是:20

    和指针类似,下面几种方式都是有效的。

    int &intRef = Number;

    int& intRef = Number;

    int & intRef = Number;

    5.2      引用的操作

    如果程序寻找引用的地址,它只能找到所引用的目标地址。

    所以下面这段程序输出的两个地址是相同的,都是Number的地址。

    void GetReferenceAddr()

    {

        int Number = 20;

        int & intRef = Number;

        cout<<&intRef<<endl;

        cout<<&Number<<endl;

    }

    C++没有提供任何访问引用本身地址的方法,因为没有意义。

    它在创建的时候就被初始化,以后的任何时候都是作为目标的别名来使用的。不能再更改引用的目标。

    5.3      什么能被引用

    5.3.1      对指针类型进行引用

    由于指针也是变量,所以能够建立指针变量的引用。

    void CreatePointerRef()

    {

        int Number = 20;

        int * intPtr  = &Number;

        cout<<*intPtr<<endl;

        cout<<*intPtr<<endl;

    }

    上面这个函数的输出为:

    20

    20

     

    l         void进行引用是不允许的

    void & a = 3 //error

    l         对数组的引用是不允许的

    void CreateArrayRef()

    {

        int Number[10];

        int & NumberRef[10] = Number; //error

    }

    l         引用本身不是一种数据类型,所以没有引用的指针;但是引用的引用还是有的。

    所谓的引用的引用其实还是引用。

    void RefOfRef()

    {

        int Number  =20;

        int & intRef = Number;

        int & intRef2 = intRef;

        //int& * refPtr = & intRef; //error

        cout<<intRef<<endl;

        cout<<intRef2<<endl;

        cout<<Number<<endl;

    }

    l         有空指针,没有空引用

    // int & ref = NULL; //error

    5.4      用引用传递函数参数

    还是那个老掉牙的swap的例子

    void MySwap(int& x, int& y)

    {

        int temp ;

        temp = x;

        x = y;

        y = temp;

    }

    5.5      用引用返回值

    注意返回的引用一定不能是局部变量的引用。下面的第二个例子就存在问题。

    int temp;

    int & ReturnRef1()

    {

        temp  = 20;

        return temp; //ok

    }

     

    int & ReturnRef2()

    {

        int temp =20;

        return temp;//error

    }

    //这种是允许的,不过以后要在合适的地方把该堆中的内存释放

    int & ReturnRef3()

    {

        int * intPtr  = new int;

        *intPtr = 20;

        return *intPtr;

    }

    5.6      函数调用作为左值

    所谓的函数调用作为左值指的就是下面这种用法,第一次看到,还是比较新鲜的。以后可以尝试使用。

    int leftValue = 0;

    int & getLeftValue()

    {

        return leftValue;

    }

    void TestLeftValue()

    {

        for(int i =0;i<5;i++)

           getLeftValue()++;  //左值运算

        cout<<leftValue<<endl;

    }

     

    6         结构

    这里的结构和delphi中的结构基本是差不多的。

    结构是一种类型,结构内成员的存放是连续的。

    存在结构的指针;

    存在结构的引用;

    6.1      给结构赋值

    例如对于下面的结构:

    struct Employee{

        string Name;

        int Age;

        string Tel;

        int Height;

    };

    //可以这样赋值

    void UseStruct1()

    {

        Employee e;

        e.Age  =27;              //赋值

        e.Height = 163;          //赋值

        e.Name = "YangGuichun";  //赋值

        e.Tel = "13266839793";   //赋值

        cout<<e.Age<<endl

           <<e.Height<<endl

           <<e.Name<<endl

           <<e.Tel<<endl;

    }

    //也可以这样赋值

    void UseStruct2()

    {

        Employee e ={"YangGuichun",27,"13266839793",163};    //赋值

        cout<<e.Age<<endl

           <<e.Height<<endl

           <<e.Name<<endl

           <<e.Tel<<endl;

    }

    //还可以这样赋值

    void UseStruct3()

    {

        Employee e ={"YangGuichun",27,"13266839793",163};   

        Employee e2 = e; //赋值

        cout<<e2.Age<<endl

           <<e2.Height<<endl

           <<e2.Name<<endl

           <<e2.Tel<<endl;

    }

    6.2      结构数组

    通过下面的程序:

    void StructArray()

    {

        Employee e ={"YangGuichun",27,"13266839793",163};   

        Employee eArray[5];

        for(int i=0;i<5;i++)

           eArray[i] = e;

        int Temp =20;

        cout<<&Temp<<endl;

        cout<<&(eArray[0].Name)<<endl;

        cout<<&(eArray[0].Height)<<endl;

        cout<<&(eArray[1].Name)<<endl;

    }

    得出以下几点结论:

    l         结构的内容是放在函数的堆栈中的;

    l         结构数组的内容是连续排放的;

    6.3      函数返回结构类型

    如果要返回结构类型的引用,一定要从堆中来开辟该结构的地址空间,下面是一个错误示例。

    Employee & ReturnStruct()

    {

        Employee e ={"YangGuichun",27,"13266839793",163};   

        cout<<"e.Age' Addr is:"<<&(e.Age)<<endl;

        cout<<"e.Age is:"<<e.Age<<endl;

        cout<<"e.Height' Addr is:"<<&(e.Height)<<endl;

        cout<<"e.Height is:"<<e.Height<<endl;

        return e;

    }

    void TestReturnStruct()

    {

        Employee & e = ReturnStruct();

        cout<<endl;

        cout<<"e.Age' Addr is:"<<&(e.Age)<<endl;

        cout<<"e.Age is:"<<e.Age<<endl;

        cout<<"e.Height' Addr is:"<<&(e.Height)<<endl;

        cout<<"e.Height is:"<<e.Height<<endl;

    }

    ReturnStruct返回的其实是函数栈中的地址。由于推出该函数之后该结构的地址也被释放,所以将会再在TestReturnStruct中使用该结构,将会发生不可预知的错误。

    正确的使用方式应该是:

    Employee & ReturnStruct2()

    {

        Employee e ={"YangGuichun",27,"13266839793",163};   

        Employee *e1 = new Employee;

        *e1 =e;

        cout<<"e1.Age' Addr is:"<<&(e1->Age)<<endl;

        cout<<"e1.Age is:"<<e1->Age<<endl;

        cout<<"e1.Height' Addr is:"<<&(e1->Height)<<endl;

        cout<<"e1.Height is:"<<e1->Height<<endl;

        return *e1;

    }

    void TestReturnStruct2()

    {

        Employee & e1 = ReturnStruct2();

        cout<<endl;

        cout<<"e1.Age' Addr is:"<<&(e1.Age)<<endl;

        cout<<"e1.Age is:"<<e1.Age<<endl;

        cout<<"e1.Height' Addr is:"<<&(e1.Height)<<endl;

        cout<<"e1.Height is:"<<e1.Height<<endl;

    }

    6.4      结构的嵌套

    结构成员不能是自身的变量,但可以是自身结构指针作为成员;

     

    7        

    7.1      类的定义

    类的定义一定要放在头文件中,类的实现可以放在头文件,也可以放在cpp文件中。

    类的实现如果放在cpp文件当中,则一定要在开始引用该类的定义头文件。

    下面是一个例子

    ///////////////////////Saving.h//////////////////////////////////////////

    #include "stdafx.h"

     

    class Saving{

    public:

        string GetName();

    private:

        string Name;

    };

    /////////////////////////Saving.cpp//////////////////////////////////////

    #include "stdafx.h"

    #include "LearnClass.h"

    string Saving::GetName()

    {

        Name = "YangGuichun";

        return Name;

    }

     

    注意:在实现成员函数的时候,Saving是放在成员函数之前,而不是返回值之前;

    7.2      类的调用

    用指针来调用成员函数

    例如,对于上面的类:

    void TestSaving(Saving * saving)

    {

        cout<<saving->GetName()<<endl;

    }

     

    //可以直接传类名

    void TestSaving2(Saving saving)

    {

        cout<<saving.GetName()<<endl;

    }

     

    //也可以用引用

    void TestSaving3(Saving & saving)

    {

        cout<<saving.GetName()<<endl;

    }

    8         堆与拷贝构造函数

    首先介绍一下几种存储空间的区别:

           全局变量、静态数据、常量存放在全局数据区中;所有类成员函数和非成员函数代码存放在代码区中;为运行函数而分配的局部变量、函数参数、返回数据、返回地址邓存放在栈区、余下的控件都被当作堆区。

    8.1      拷贝构造函数(重要)

    8.1.1      什么是拷贝构造函数

    这个概念对我来说也是新的,先给个例子:

    void TestSaving4()

    {

        Saving s = Saving("YangGuichun");

        Saving saving2 = s;

        cout<<saving2.GetName()<<endl;

    }

    输出为:YangGuichun

    所谓的拷贝构造函数就是在构造一个对象的时候用一个对象去构造另一个对象。例如上面用s去构造saving2

    8.1.2      拷贝构造函数在类的值参传递中的应用

    通过这个特性,开始有点明白为什么要有类指针了。下面给出一个例子。

    void PrintAndChangeSavingName(Saving s)

    {

        cout<<s.GetName()<<endl;

        s.SetName("YangGuichun2");

    }

    void TestCopyConstructor()

    {

        Saving s = Saving("Yangguichun");

        PrintAndChangeSavingName(s);

        cout<<s.GetName()<<endl;

    }

    如果调用TestCopyConstructor(),那么输出将是:

    YangGuichun

    YangGuichun

    而不会是:

    YangGuichun

    YangGuichun2

    这里就是拷贝函数在起作用了,由于在PrintAndChangeSavingName(..)的参数申明的是类类型的变量,而不是类指针,所以在TestCopyConstructor()中将s传递给PrintAndChangeSavingName()的时候,将会按照拷贝构造函数的原理创建一个新的对象。

    这也正是在c++中要使用类指针的原因了。

    8.1.3      默认拷贝构造函数

    和定义构造函数一样,拷贝构造函数是要定义的。

    如果不定义,那么系统会自动为每个类指定一个默认的拷贝构造函数,在默认的拷贝构造函数中,将完成一个成员一个成员的拷贝。

    class Student

    {

    public:

        Student(std::string Name)

        {

           this->Name = Name;

           cout<<"in the Student(std::string Name) "<<endl;

        }

        Student()

        {

           this->Name = "yangguichun";

           cout<<"in the Student() "<<endl;

        }

     

        Student(Student& s)//拷贝构造函数

        {

           this->Name = s.Name;

           cout<<"in the Student(Student& s)"<<endl;

        }

        string GetName()

        {

           return Name;

        }

    private:

        string Name;

    };

    8.1.4      定义拷贝构造函数

    在默认拷贝构造函数中,拷贝的策略是逐个成员依次拷贝。但是,一个类可能会拥有资源,当其构造函数分配了一个资源(如打印机)的时候,会发生什么呢?两个对象共同拥有一个资源,可能会造成冲突。

    如果你的类需要一个析构函数来释放资源,那么它需要一个拷贝构造函数。

    8.1.5      对象的各种创建方式

     

     

    9         静态成员与友元

    首先要说明的是,这里的静态成员和delphi里头的静态成员基本没有什么差别。不过可能在定义上会有一些不同。

    9.1      静态成员的定义

    下面是一个静态数据成员定义的例子:

    ////////////////////////////LearnStaticMember.h////////////////////////

    class CLearnStaticMember

    {

    public:

        CLearnStaticMember(void);

        int GetCounter();

    public:

        ~CLearnStaticMember(void);

    private:

        static int Counter;

    };

    ////////////////////////////LearnStaticMember.cpp////////////////////////

    #include "StdAfx.h"

    #include "LearnStaticMember.h"

     

    int CLearnStaticMember::Counter = 0;     //初始化静态数据成员

    CLearnStaticMember::CLearnStaticMember(void)

    {

        this->Counter++; 

    }

     

    CLearnStaticMember::~CLearnStaticMember(void)

    {

        this->Counter--;

    }

    int CLearnStaticMember::GetCounter()

    {

        return Counter;

    }

    注意到上面的Counter的初始化代码是放置在LearnStaticMember的实现文件中的。

    9.2      静态数据成员的调用

    加入现在将上节中的Counter数据成员改为public的,代码如下:

    ////////////////////////////LearnStaticMember.h////////////////////////

    class CLearnStaticMember

    {

    public:

        CLearnStaticMember(void);

        int GetCounter();

    public:

        ~CLearnStaticMember(void);

    public:

        static int Counter;

    };

    ////////////////////////////LearnStaticMember.cpp////////////////////////

    #include "StdAfx.h"

    #include "LearnStaticMember.h"

     

    int CLearnStaticMember::Counter = 0;     //初始化静态数据成员

    CLearnStaticMember::CLearnStaticMember(void)

    {

        this->Counter++; 

    }

     

    CLearnStaticMember::~CLearnStaticMember(void)

    {

        this->Counter--;

    }

    int CLearnStaticMember::GetCounter()

    {

        return Counter;

    }

    那么可以像下面这样使用:

    void TestStatic()

    {

        cout<<CLearnStaticMember::Counter<<endl; //推荐的调用方式

        CLearnStaticMember aObj1;

        cout<<aObj1.Counter<<endl;

        CLearnStaticMember aObj2;

        cout<<CLearnStaticMember.Counter<<endl;  //不推荐使用

    }

    9.3      静态成员函数的定义和调用

    /////////////////////////////////LearnStaticMethod.h///////////////////////

    #pragma once

     

    class CLearnStaticMethod

    {

    public:

        CLearnStaticMethod(void);

        void static AStaticMethod();

    public:

        ~CLearnStaticMethod(void);

    };

    /////////////////////////////////LearnStaticMethod.cpp///////////////////////

    #include "StdAfx.h"

    #include "LearnStaticMethod.h"

     

    CLearnStaticMethod::CLearnStaticMethod(void)

    {

    }

     

    CLearnStaticMethod::~CLearnStaticMethod(void)

    {

    }

     

    void CLearnStaticMethod::AStaticMethod()

    {

        cout<<"this is in the static method"<<endl;

    }

    在头文件中定义静态方法的时候使用了static,而在cpp文件中实现的时候并不需要使用static

    void TestStaticMethod()

    {

        CLearnStaticMethod::AStaticMethod();

        //CLearnStaticMethod.AStaticMethod();  //error

    }

    访问静态函数的时候,只能用全局操作符(::)

    9.4      友元

    普通函数需要直接访问类的保护和私有数据成员的原因主要是为了提高效率。

    需要友元的另外一个原因是为了重载操作符;

    9.4.1      友元函数的定义

    ////////////////////////LearnFriendMetho.h///////////////////////////

    #pragma once

     

    class LearnFriendMethod

    {

    public:

        LearnFriendMethod(void);

        friend void FriendMethod();

    public:

        ~LearnFriendMethod(void);

    private:

        string Name;

    };

     

    ////////////////////////LearnFriendMetho.cpp///////////////////////////

    #include "StdAfx.h"

    #include "LearnFriendMethod.h"

     

    LearnFriendMethod::LearnFriendMethod(void)

    {

        this->Name = "yangcuichun";

    }

     

    LearnFriendMethod::~LearnFriendMethod(void)

    {

    }

     

    void FriendMethod()

    {

        cout<<"this is the Friend Method"<<endl;

        LearnFriendMethod aObj;

        cout<<"aObj'Name is"<<aObj.Name<<endl;

    }

     

    该函数的输出为:

    this is the Friend Method

    aObjName isyangguichun

    9.4.2      一个类的成员函数可以是另外一个类的友元

    /////////////////////////////////LearnFriendMethod2.h///////////////////////

    #pragma once

     

    class LearnFriendMethod2

    {

    public:

        LearnFriendMethod2(void);

        void AFriendMethod();

    public:

        ~LearnFriendMethod2(void);

    };

     

    class LearnFriendMethod3

    {

    public:

        LearnFriendMethod3(void);

        friend void LearnFriendMethod2::AFriendMethod();

    public:

        ~LearnFriendMethod3(void);

    private:

        string Name;

    };

     

    /////////////////////////////////LearnFriendMethod2.cpp///////////////////////

    #include "StdAfx.h"

    #include "LearnFriendMethod2.h"

     

    LearnFriendMethod2::LearnFriendMethod2(void)

    {

    }

     

    LearnFriendMethod2::~LearnFriendMethod2(void)

    {

    }

     

    LearnFriendMethod3::LearnFriendMethod3(void)

    {

        this->Name = "LearnFriendMethod3";

    }

     

    LearnFriendMethod3::~LearnFriendMethod3(void)

    {

    }

     

    void LearnFriendMethod2::AFriendMethod()   //友元方法的定义

    {

        cout<<"this is in the void LearnFriendMethod2::AFriendMethod()"<<endl;

        LearnFriendMethod3 aObj;

        cout<<"aObj's Name is :"<<aObj.Name<<endl;

    }

     

    如果执行下面的测试程序之后:

    void TestClassFriendMethod()

    {

        LearnFriendMethod2 aObj;

        aObj.AFriendMethod();

    }

    输出的结果应该是:

    this is in the void LearnFriendMethod2::AFriendMethod()

    aObj's Name is :LearnFriendMethod3

     

    9.4.3      整个类可以是另外一个类的友元

    为了验证友元是不是相互的,我谢了下面这个例子:

    ////////////////////////////////////LearnFriendClass.h////////////////////

    #pragma once

     

    class LearnFriendClass2;

    class LearnFriendClass1

    {

    public:

        LearnFriendClass1(void);

        string GetFriendName(LearnFriendClass2* obj);

    public:

        ~LearnFriendClass1(void);

    private:

        string Name;

    };

    class LearnFriendClass2

    {

    public:

        LearnFriendClass2(void);

        friend class LearnFriendClass1;

        string GetFriendName(LearnFriendClass1* obj);

    public:

        ~LearnFriendClass2(void);

    private:

        string Name;

    };

    //////////////////////////////////LearnFriendClass.cpp/////////////////

    #include "StdAfx.h"

    #include "LearnFriendClass.h"

     

    LearnFriendClass1::LearnFriendClass1(void)

    {

    }

     

    LearnFriendClass1::~LearnFriendClass1(void)

    {

    }

     

    LearnFriendClass2::LearnFriendClass2(void)

    {

    }

     

    LearnFriendClass2::~LearnFriendClass2(void)

    {

    }

     

    string LearnFriendClass1::GetFriendName(LearnFriendClass2* obj)

    {

        cout<<obj->Name<<endl;

    }

    string LearnFriendClass2::GetFriendName(LearnFriendClass1* obj)

    {

        //cout<<obj->Name<<endl; //error

    }

  • 相关阅读:
    HR问“你目前有几个offer”,聪明人会怎么说?
    秋招还有 1 个月到达战场,请做好准备 !
    我人生中的第一场Java面试
    MZ头里面的东西。真他妈多
    特殊的一卦
    今天出门去办事,又倒霉了
    内核回调
    sys_call_table HOOK
    起一卦,看看情况
    我的简陋界面库的模块组成
  • 原文地址:https://www.cnblogs.com/strinkbug/p/640006.html
Copyright © 2011-2022 走看看