C++编程指向成员的指针以及this指针的基本使用指南
指向成员的指针
指向成员的指针的声明是指针声明的特例。使用以下序列来声明它们:
[storage-class-specifiers] [cv-qualifiers] type-specifiers [ms-modifier] qualified-name ::* [cv-qualifiers] identifier [= & qualified-name :: member-name];
声明说明符:
- 可选存储类说明符。
- 可选 const 和/或 volatile 说明符。
- 类型说明符:类型的名称。这是要指向的成员的类型,而不是类。
声明符:
- 可选的 Microsoft 专用修饰符。
- 包含要指向的成员的类的限定名。
- :: 运算符。
- * 运算符。
- 可选 const 和/或 volatile 说明符。
- 命名指向成员的指针的标识符。
可选的初始值设定项:
- = 运算符。
- & 运算符。
- 类的限定名。
- :: 运算符。
- 适当类型的类的非静态成员的名称。
像往常一样,允许在单个声明中使用多个声明符(以及任何关联的初始值设定项)。
指向类的成员的指针与普通指针不同,因为它有该成员的类型的类型信息和该成员所属的类的类型信息。常规指针只标识内存中的一个对象或只具有其地址。指向类的某个成员的指针标识类的所有实例中的该成员。以下示例声明类、Window 和一些指向成员数据的指针。
// pointers_to_members1.cpp class Window { public: Window(); // Default constructor. Window( int x1, int y1, // Constructor specifying int x2, int y2 ); // window size. bool SetCaption( const char *szTitle ); // Set window caption. const char *GetCaption(); // Get window caption. char *szWinCaption; // Window caption. }; // Declare a pointer to the data member szWinCaption. char * Window::* pwCaption = &Window::szWinCaption; int main() { }
在前面的示例中,pwCaption 是一个指针,它指向具有 Windowchar* 类型的类 的任何成员。类型 pwCaption 不是 char * Window::*。下一个代码片段将指针声明为 SetCaption 和 GetCaption 成员函数。
const char * (Window::*pfnwGC)() = &Window::GetCaption; bool (Window::*pfnwSC)( const char * ) = &Window::SetCaption;
指针 pfnwGC 和 pfnwSC 分别指向 GetCaption 类的 SetCaption 和 Window。以下代码直接使用指向成员 pwCaption 的指针将信息复制到窗口标题:
Window wMainWindow; Window *pwChildWindow = new Window; char *szUntitled = "Untitled - "; int cUntitledLen = strlen( szUntitled ); strcpy_s( wMainWindow.*pwCaption, cUntitledLen, szUntitled ); (wMainWindow.*pwCaption)[cUntitledLen - 1] = '1'; //same as //wMainWindow.SzWinCaption [cUntitledLen - 1] = '1'; strcpy_s( pwChildWindow->*pwCaption, cUntitledLen, szUntitled ); (pwChildWindow->*pwCaption)[cUntitledLen - 1] = '2'; //same as //pwChildWindow->szWinCaption[cUntitledLen - 1] = '2';
.* 和 –>* 运算符(指向成员的指针运算符)的区别在于 .* 运算符选择成员给定的对象或对象引用,而 –>* 运算符通过指针选择成员。(有关这些运算符的更多信息,请参阅使用指向成员的指针运算符的表达式。)
指向成员的指针运算符的结果是成员的类型 - 本例中为 char *。
以下代码片段使用指向成员的指针调用成员函数 GetCaption 和 SetCaption:
// Allocate a buffer. enum { sizeOfBuffer = 100 }; char szCaptionBase[sizeOfBuffer]; // Copy the main window caption into the buffer // and append " [View 1]". strcpy_s( szCaptionBase, sizeOfBuffer, (wMainWindow.*pfnwGC)() ); strcat_s( szCaptionBase, sizeOfBuffer, " [View 1]" ); // Set the child window's caption. (pwChildWindow->*pfnwSC)( szCaptionBase );
针对指向成员的指针的限制
静态成员的地址不是指向成员的指针。它是指向静态成员的一个实例的常规指针。由于给定类的所有对象只存在一个静态成员实例,因此可以使用普通的 address-of (&) 和取消引用 (*) 运算符。
指向成员和虚函数的指针
通过指向成员函数的指针调用虚函数就如同直接调用函数一样;将在 v 表中查找并调用正确的函数。
一直以来,虚函数工作的关键是通过指向基类的指针来调用它们。(有关虚函数的详细信息,请参阅虚函数。)
以下代码演示如何通过指向成员函数的指针调用虚函数:
// virtual_functions.cpp // compile with: /EHsc #include <iostream> using namespace std; class Base { public: virtual void Print(); }; void (Base ::* bfnPrint)() = &Base :: Print; void Base :: Print() { cout << "Print function for class Base\n"; } class Derived : public Base { public: void Print(); // Print is still a virtual function. }; void Derived :: Print() { cout << "Print function for class Derived\n"; } int main() { Base *bPtr; Base bObject; Derived dObject; bPtr = &bObject; // Set pointer to address of bObject. (bPtr->*bfnPrint)(); bPtr = &dObject; // Set pointer to address of dObject. (bPtr->*bfnPrint)(); } //Output: Print function for class Base Print function for class Derived
this 指针
this 指针是只能在 class、struct或 union 类型的非静态成员函数中访问的指针。它指向为其调用成员函数的对象。静态成员函数没有 this 指针。
语法
this this->member-identifier
备注
对象的 this 指针不是对象的一部分;它没有在对象上的 sizeof 语句的结果中反映。相反,当对某个对象调用非静态成员函数时,该对象的地址将由编译器作为隐藏的参数传递给函数。例如,以下函数调用:
myDate.setMonth( 3 );
可以按以下方式解释:
setMonth( &myDate, 3 );
对象的地址可从成员函数的内部作为 this 指针提供。 this 的大多数使用都是隐式的。在引用类的成员时显式使用 this 是合法的,但没有必要。例如:
void Date::setMonth( int mn ) { month = mn; // These three statements this->month = mn; // are equivalent (*this).month = mn; }
表达式 *this 通常用于从成员函数返回当前对象:
return *this;
this 指针还用于防止自引用:
if (&Object != this) { // do not execute in cases of self-reference
注意
由于 this 指针无法更改,因此不允许对 this 赋值。C++ 的早期实现允许对 this 赋值。
this 指针有时可直接使用 - 例如,当操作自引用数据结构,而其中需要当前对象的地址时。
// this_pointer.cpp // compile with: /EHsc #include <iostream> #include <string.h> using namespace std; class Buf { public: Buf( char* szBuffer, size_t sizeOfBuffer ); Buf& operator=( const Buf & ); void Display() { cout << buffer << endl; } private: char* buffer; size_t sizeOfBuffer; }; Buf::Buf( char* szBuffer, size_t sizeOfBuffer ) { sizeOfBuffer++; // account for a NULL terminator buffer = new char[ sizeOfBuffer ]; if (buffer) { strcpy_s( buffer, sizeOfBuffer, szBuffer ); sizeOfBuffer = sizeOfBuffer; } } Buf& Buf::operator=( const Buf &otherbuf ) { if( &otherbuf != this ) { if (buffer) delete [] buffer; sizeOfBuffer = strlen( otherbuf.buffer ) + 1; buffer = new char[sizeOfBuffer]; strcpy_s( buffer, sizeOfBuffer, otherbuf.buffer ); } return *this; } int main() { Buf myBuf( "my buffer", 10 ); Buf yourBuf( "your buffer", 12 ); // Display 'my buffer' myBuf.Display(); // assignment opperator myBuf = yourBuf; // Display 'your buffer' myBuf.Display(); }
输出:
my buffer your buffer
this 指针的类型
通过 const 和 关键字,可以在函数声明中修改 volatilethis 指针的类型。若要将函数声明为具有一个或多个关键字的特性,请将关键字添加到函数参数列表的后面。
请看以下示例:
// type_of_this_pointer1.cpp class Point { unsigned X() const; }; int main() { }
上面的代码声明一个成员函数 X,其中,this 指针被视为指向 const 对象的 const 指针。可以使用 cv-mod-list 选项的组合,但它们始终通过 this 修改指向的对象,而不是 this 指针本身。因此,以下声明将声明函数 X;this 指针是指向 const 对象的 const 指针:
// type_of_this_pointer2.cpp class Point { unsigned X() const; }; int main() { }
成员函数中 this 指针的类型由以下语法描述,其中,cv-qualifier-list 是从成员函数声明符中确定的,可以是 const 和/或 volatile,而 class-type 是类的名称:
[cv-qualifier-list] class-type * const this
换言之,this 始终是 const 指针;无法重新指派它。成员函数声明中使用的 const 或 volatile 限定符适用于由该函数范围中的 this 指向的类实例。
下表介绍了有关这些修饰符的工作方式的更多信息。
this 修饰符的语义
修饰符 | 含义 |
---|---|
const | 不能更改数据成员;无法调用不是 const 的成员函数。 |
volatile | 每次访问成员数据时,都会从内存中加载该数据;禁用某些优化。 |
将 const 对象传递给不是 const 的成员函数是错误的。同样,将 volatile 对象传递给不是 volatile 的成员函数也是错误的。
声明为 const 的成员函数不能更改数据成员 - 在此类函数中,this 指针是指向 const 对象的指针。
注意
构造函数和析构函数不能声明为 const 或 volatile。但是,可以在 const 或 volatile 对象上调用它们。