上一讲鸡啄米给大家讲的是指针用作函数参数、指针型函数和函数指针的知识,今天鸡啄米把指针方面的内容结个尾,讲讲对象指针。

       一.什么是对象指针

       每个变量都占有一定的内存空间,对象同样也需要占用内存空间。对象有数据成员函数成员两种成员,但是实际上只有对象的数据成员会占用内存,函数成员则不会。我们可以通过对象名引用对象,也可以通过对象地址访问对象。对象指针就是存储对象地址的变量。声明对象指针的方式与一般的指针类似:

       类名 *对象指针名;

       使用对象名可以方便的访问对象的公有成员,同样使用对象指针也可以很容易的访问对象的公有成员。用对象指针访问对象的公有成员的形式为:

       对象指针名->公有成员名;

       鸡啄米让大家看一个简单的对象指针的例子:

       #include <iostream>
       using namespace std;
       class CStudent
       {
       public:
                 CStudent(int nAge=15)   { m_nAge = nAge; }       // 构造函数
                 int GetAge()            { return m_nAge; }       // 内联函数,返回m_nAge
       private:
                 int m_nAge;        // 私有数据
       };
       int main()
       {
                CStudent student(17);      // 声明对象student并对其初始化
                CStudent *ptr;             // 声明对象指针
                ptr = &student;            // 初始化对象指针
                cout << student.GetAge() << endl;   // 通过对象名访问对象的成员
                cout << ptr->GetAge() << endl;      // 通过对象指针访问对象的成员
                return 0;
       }

       跟一般指针一样,对象指针在使用之前也必须先赋值,因为它必须先明确指向一个对象才能正常使用,否则可能会由于被赋与了随机值而有可能访问到重要地址破坏系统数据。上面的程序运行结果是:

      17
      17

       二.this指针

       this指针实际上就隐含于类的成员函数中,它指向成员函数正在操作的对象。构造函数和析构函数也不例外,也同样隐含了this指针。它也是一个指针,只是有点特殊。

       比如上面的CStudent类的成员函数GetAge中的语句:return m_nAge;对于编译器来说相当于执行return this->m_nAge;。为什么要有这个this指针呢?因为在执行这条语句时编译器需要知道返回的m_nAge到底属于哪一个对象,编译器事先已经把对象地址赋给this指针,调用成员函数操作数据成员时就隐含使用了这个this指针,这样就确定了访问的m_nAge属于哪个对象。

       一般在软件开发中不会直接用到this指针访问数据成员,有时需要将此对象作为参数传递给某个函数时会使用this指针,亦或是写程序时忘记某个函数名,输入this->后vs2010会提示出所有成员函数。

鸡啄米:C++编程入门系列之三十一(数组、指针和字符串:对象指针)

       三.指向类的非静态成员的指针

       类的成员都是变量、对象、函数等,我们同样可以定义存放它们的地址的指针,从而使指针指向对象的成员,通过指针就可以访问对象的成员了。但是通过这种指向成员的指针一样也只能访问公有成员。

       声明指向非静态成员的指针的语法形式为:

       类型说明符 类名::*指针名;                    // 声明指向公有数据成员的指针
       类型说明符 (类名::*指针名)(形参表);          // 声明指向公有成员函数的指针

       指向成员的指针也要先声明再赋值,然后才能引用。给指向成员的指针赋值就是要说明此指针指向类的哪一个成员。

       为指向数据成员的指针赋值的语法形式为:

       指针名 = &类名::数据成员名;

       在前面的地址相关运算中,鸡啄米讲到,用“&”运算符可以取到变量的地址,将其赋值给某个指针后就可以通过这个指针访问变量了。但是对于指向类的数据成员的指针就不同了,在类声明时并不会为类的数据成员分配内存空间,所以给指向数据成员的指针赋值只是确定了此指针指向哪个数据成员,同时也在指针中存放了该数据成员相对于类的起始地址的地址偏移量。这时通过赋值后的指针不能访问到具体的数据成员。

       在声明了类的对象后就会为对象分配内存,这样根据对象的起始地址和指向数据成员的指针中存放的偏移量就可以访问到对象的数据成员了。通过对象和指向数据成员的指针访问公有数据成员的语法形式为:

       对象名.*指向数据成员的指针名
       或者 对象指针名->*指向数据成员的指针名

       为指向成员函数的指针赋值的语法形式为:

       指针名 = &类名::函数成员名;

       在上面的形式为成员函数指针赋值以后还不能直接用此指针调用成员函数,必须先声明了类的对象,再通过对象和指向非静态成员函数的指针调用成员函数。调用的语法形式为:

       (对象名.*指向成员函数的指针名)(形参表)
       或者 (对象指针名->*指向成员函数的指针名)(形参表)

       在为成员函数指针赋值时,还有用对象和成员函数指针调用成员函数时,我们都要注意成员函数指针的返回值类型和形参表一定要和指向的成员函数一致。

       鸡啄米再以上面对象指针中的例子为基础修改下,让大家看看访问对象公有成员函数的几种方式:

       int main()
       {
                 CStudent student(17);      // 声明对象student并对其初始化
                 CStudent *ptr;             // 声明对象指针
                 int (CStudent::*pGetAge)();// 声明指向成员函数GetAge的指针
                 pGetAge = &CStudent::GetAge;// 为pGetAge赋值
                 ptr = &student;             // 初始化对象指针
                 cout << student.GetAge() << endl;   // 通过对象名访问成员函数
                 cout << ptr->GetAge() << endl;      // 通过对象指针访问对象的成员函数
                 cout << (student.*pGetAge)() << endl; // 通过成员函数指针访问成员函数
                 return 0;
        }

       上面的例子中,先声明了一个CStudent类的对象student并初始化,又分别声明了一个指向对象student的指针ptr和指向成员函数GetAge的指针并各自初始化后,分别通过对象名、对象指针和指向成员函数的指针这三种方式访问公有成员函数GetAge。程序的运行结果是:

       17
       17
       17

       四.指向类的静态成员的指针

       上面说了类的非静态成员的指针的概念和使用方法,对于类的静态成员,我们可以用普通指针存放它的地址,通过普通指针访问它。

       鸡啄米再把CStudent类的例子修改下,说明怎样通过指向类的静态数据成员的指针访问静态数据成员:

       #include <iostream>
       using namespace std;
       class CStudent
       {
       public:
                   CStudent(int nAge=15)   { m_nAge = nAge; m_nCount++; }       // 构造函数
                   CStudent(CStudent &stu);                         // 拷贝构造函数
                   int GetAge()            { return m_nAge; }       // 内联函数,返回m_nAge
                   static int m_nCount;   // 静态数据成员声明
       private:
                   int m_nAge;          // 私有数据
       };
       CStudent::CStudent(CStudent &stu)
       {
                  m_nAge = stu.m_nAge;
                  m_nCount++;
       }
       int CStudent::m_nCount = 0;   // 静态数据成员初始化
       int main()
       {
                 int *pCount = &CStudent::m_nCount;  // 声明一个int型的指针,指向静态数据成员m_nCount
                 CStudent student1(17);              // 声明对象student1并对其初始化    
                 cout << "student1:" << student1.GetAge() << endl;
                 cout << "student id=" << *pCount << endl;      // 通过指针访问静态数据成员
                 CStudent student2(student1);                   // 声明对象student2并用student1初始化它
                 cout << "student2:" << student2.GetAge() << endl;
                 cout << "student id=" << *pCount << endl;      // 通过指针访问静态数据成员
                 return 0;
       }

       程序运行结果是:

       student1:17
       student id=1
       student2:17
       student id=2

       这一讲的内容就这些了,根据鸡啄米的经验,指向类的非静态成员的指针和指向类的静态成员的指针其实并不常用,大家可以重点看对象指针和this指针。如果有任何问题可以到鸡啄米博客留言讨论,希望我们在交流中一块提高。