this指针(C++ primer)

来源:互联网 发布:stm32单片机是什么 编辑:程序博客网 时间:2024/05/16 17:43

每个类对象都将维护自己的类数据成员的拷贝例如
int main() {
Screen myScreen( 3, 3 ), bufScreen;
myScreen.clear();
myScreen.move( 2, 2 );
myScreen.set( '*' );
myScreen.display();
bufScreen.reSize( 5, 5 );

bufScreen.display();
}

myScreen
有自己的_screen _height _width _cursor 数据成员。BufScreen 也有自己独立的一组成员,仍是每个类成员函数只存在一份拷贝。myScreen bufScreen 都调用任何特定成员函数的同一份拷贝。我们在上节已经看到成员函数可以引用自己的类成员而无需使用成员访问操作符。例如函数move()的定义如下
inline void Screen::move( int r, int c )
{
if ( checkRange( r, c ) ) //
无效位置?
{
int row = (r-1) * _width; //
行位置
_cursor = row + c - 1;
}
}

如果调用了对象myScreen 的函数move() ,那么在move()中访问的数据成员_width _cursor myScreen 的数据成员,如果调用了对象bufScreen 的函数move() ,则访问的是bufScreen 的数据成员move(),操纵的数据成员_cursor ,怎样被依次绑定到属于myScreen bufScreen 的数据成员上呢,简短地回答起来就是用this 指针。

每个类成员函数都含有一个指向被调用对象的指针,这个指针被称为this 。在非const成员函数中,它的类型是指向该类类型的指针。在const 成员函数中,是指向const 类类型的指针。而在volatile 成员函数中,是指向volatile 类类型的指针。例如在类Screen 的成员函数move()中,this 指针的类型是Screen* 。在类List 的非const 成员函数中,this 指针的类型是List*
因为this 指针指向要调用其成员函数的类对象,所以如果函数move()被对象myScreen调用,则this 指针指向对象myScreen 。类似地,如果函数move()被对象bufScreen调用,则this 指针指向对象bufScreen Move()操纵的数据成员、_cursor 依次被绑定在属于myScreenbufScreen 的数据成员上。
要想理解这一点,一种方法是看一看编译器是怎样实现this 指针的。为支持this 指针
必须要应用两个转变:
1
改变类成员函数的定义。用额外的参数this 指针来定义每个成员函数,例如:
// 伪代码, 说明编译器对一个成员函数定义的展开形式
//
不是合法的 C++ 代码
inline void move( Screen* this, int r, int c )
{
if ( checkRange( r, c ) )
{
int row = (r-1) * this->_width;
this->_cursor = row + c - 1;
}
}

在这个成员函数定义中显式使用this 指针来访问类数据成员_width _cursor
2
改变每个类成员函数的调用加上一个额外的实参——被调用对象的地址例如

myScreen.move( 2, 2)
被转化为
move( &myScreen, 2, 2 )
程序员可以在成员函数定义中显式地引用this 指针例如如下定义成员函数home()是合法的,尽管这样做不是必要的。
inline void Screen::home()
{
this->_cursor = 0;
}

但是有一种情况下确实需要程序员显式地引用this 指针,比如我们在前面定义的Screen成员函数copy() 下一小节将给出一些示例。
13.4.1
何时使用this 指针
函数main()在对象myScreen bufScreen 上调用类Screen 的成员函数这样的动作是在独立的语句中,我们可以重新定义Screen 类的成员函数,这样当多个成员函数应用到同一个Screen 对象上时我们可以将成员函数调用连接起来。例如在函数main()中的调用可以写为
int main() {
// ...
myScreen.clear().move(2,2).set('*').display();
bufScreen.reSize(5,5).display();
}

这看起来似乎符合操作Screen 对象的直观方式,这里用到的动作序列是先清Screen对象myScreen 然后把光标移到位置(2,2) 接着再把该位置的字符设为* ,最后显示结果
成员访问操作符点. 和箭头-> 是左结合操作符,当看到这些操作符序列时执行的顺序是从左到右,例如先调用的是myScreen.clear() ,然后是myScreen.move()等等。为使myScreen.move()myScreen.clear()之后被调用,clear()必须返回类对象myScreen 成员函数clear()的定义必须返回被调用的类对象。正如我们已经看到的,在类成员函数内是通过this指针来访问该类对象的。下面是clear()的实现:
// clear() 的声明在类体内
//
它指定了缺省实参 bkground = '#'
Screen& Screen::clear( char bkground )
{ //
重置 cursor 以及清屏幕
_cursor = 0;
_screen.assign( //
赋给字符串
_screen.size(), // size()
个字符
bkground //
值都是 bkground
);
//
返回被调用的对象
return *this;

}
注意这个成员函数的返回类型是Screen& 它表示该成员函数返回一个引用,指向它自己所属类类型的对象。为了允许在main()中连接Screen 的成员函数,成员函数move()set()也需要作修改,它们的返回类型也必须从void 改变成Screen& 且必须在函数定义中返回*this,类似地Screen 成员函数display()必须重新实现如下
Screen& Screen::display()
{
typedef string::size_type idx_type;
for ( idx_type ix = 0; ix < _height; ++ix )
{ //
针对每一行
idx_type offset = _width * ix; // row position
for ( idx_type iy = 0; iy < _width; ++iy )
//
针对每一列, 输出元素
cout << _screen[ offset + iy ];
cout << endl;
}
return *this;
}

Screen
的成员函数resize()必须如下实现
// reSize() 的声明在类体内
//
它指定了缺省实参 bkground = '#'
Screen& Screen::reSize( int h, int w, char bkground )
{ //
把屏幕的大小设置到高度 h 宽度 w
//
记住屏幕的内容
string local(_screen);
//
替换 _screen 所引用的字符串
_screen.assign( //
赋给字符串
h * w, // h * w
个字符
bkground //
值都是 bkground
);

typedef string::size_type idx_type;
idx_type local_pos = 0;
//
把原来屏幕的内容拷贝到新的屏幕上
for ( idx_type ix = 0; ix < _height; ++ix )
{ //
每一行
idx_type offset = w * ix; //
行位置
for ( idx_type iy = 0; iy < _width; ++iy )
//
每一列, 赋以原来的值
_screen[ offset + iy ] = local[ local_pos++ ];
}

_height = h;
_width = w;
// _cursor
保持不变
return *this;
}
在成员函数中,this 指针的用处不全是返回该成员函数被应用的对象,在13.3 节介绍成员函数copy()时我们看到了this 指针的另一种用法
void Screen::copy( const Screen& sobj )
{
//
如果 Screen 对象与 sobj 是同一个对象
//
无需拷贝
if ( this != &sobj )
{
//
sobj 的值拷贝到 *this
}
}

this 指针含有被调用的类对象的地址,如果sobj 指向的对象的地址与this 指针值相等,则sobj this 指向同一对象拷贝,动作是不必要的。我们会在14.7 节介绍拷贝赋值操作符时再次看到这种结构。

原创粉丝点击