您的位置:首页 > 编程语言 > C语言/C++

C++ Primer 学习笔记_49_类与数据抽象 -暗含的this指针

2016-05-07 09:28 706 查看



--隐含的this指针

引言:

 
  在前面提到过,成员函数具有一个附加的隐含形参,即指向该类对象的一个指针。这个隐含形参命名为this,与调用成员函数的对象绑定在一起。成员函数不能定义this形参,而是有编译器隐含地定义。成员函数可以显式的使用this指针,但不是必须这么做。

1、何时使用this指针

 
  有一种情况下,我们必须显式使用this指针:当需要将一个对象作为整体引用而不是引用对象的一个成员时。

 
  比如在类Screen中定义两个操作:set和move,可以使得用户将这些操作的序列连接成一个单独的表达式:

myScreen.move(4,0).set('#');
//其等价于
myScreen.move(4.0);
myScreen.set('#');


2、返回*this

 
  在单个表达式中调用move和 set操作时,每个操作必须返回一个引用,该引用指向执行操作的那个对象:

class Screen
{
public:
Screen &move(index r,index c);
Screen &set(char);
Screen &set(index,index,char);
};


这样,每个函数都会返回调用自己的那个对象。使用this指针可以用来访问该对象:

Screen &Screen::set(char c)
{
contents[cursor] = c;
return *this;
}

Screen &Screen::move(index r,index c)
{
index row = r * width;
cursor = row + c;
return *this;
}


3、从const成员返回*this

 
  在普通的非const成员函数中,this的类型是一个指向类类型的const指针。可以改变this所指向的值,但不能改变this所保存的地址。在const成员函数中,this的类型是一个指向const类类型对象的const指针。既不能改变this所指向的对象,也不能改变this所保存的地址。

 
  不能从const成员函数返回指向类对象的普通引用。const成
员函数只能返回*this作为一个 const引用。

 
  我们可以给Screen类增加一个const成员函数:display操作。如果将display作为 Screen的 const成员,则 display内部的 this指针将是一个constScreen* 型的const。然而:

myScreen.move(4,0).set('#').display(cout);	//OK
myScreen.display().set('*');	//Error


问题在于这个表达式是在由display返回的对象上运行set。该对象是const,因为display将其对象作为const返回。我们不能在const对象上调用set。

4、基于const的重载

 
  为了解决以上问题,我们必须定义两个display操作:一个是const,一个不是const。基于成员函数是否为const,可以重载一个成员函数;同样的,基于一个指针形参是否指向const,也可以重载一个函数。非const对象可以使用任一成员,但非const版本是一个更好的匹配。

class Screen
{
public:
//As before

Screen &display(std::ostream &os)
{
do_display(os);
return *this;
}
const Screen &display(std::ostream &os) const
{
do_display(os);
return *this;
}

private:
void do_display(std::ostream &os) const
{
os << contents;
}
//As before
};


调用:

Screen myScreen(5,3);
const Screen blank(5,3);
myScreen.set('#').display(cout);    //调用非const版本
blank.display(cout);                //调用const版本


5、可变数据成员

 
  有时,我们希望类的数据成员(甚至是在const成员函数中)可以修改。这可以通过将它们声明为mutable来实现。

 
  可变数据成员永远都不能为const,甚至当它们是const对象的成员时也如此。因此,const成员函数可以改变mutable成员。

class Screen
{
public:
//...

private:
mutable size_t access_ctr;

//使用access_ctr来跟踪Screen成员函数的调用频度
void do_display(std::ostream &os) const
{
++ access_ctr;      //OK
os << contents;
}
};


【建议:用于公共代码的私有实用函数】

   使用私有实用函数(如前面的do_display)的好处:

 
  1)一般愿望是避免在多个地方编写同样的代码。

 
  2)display操作预期会随着类的演变而变得复杂。当涉及到的动作变得更复杂时,只在一处而不是两处编写这些动作有更显著的意义。

 
  3)很可能我们会希望在开发时给do_display增加调试信息,这些调试信息将会在代码的最终成品版本中去掉。如果只需要改变一个do_display的定义来增加或删除调试代码,这样做将更容易。
4)这个额外的函数调用不需要涉及任何开销。我们使do_display成为内联的,所以调用do_display与将代码直接放入display操作的运行时性能应该是相同的。

P379习题12.13
//1. in screen.h
#ifndef SCREEN_H_INCLUDED
#define SCREEN_H_INCLUDED
#include <string>

class Screen
{
public:
typedef std::string::size_type index;

Screen &move(index r,index c);
Screen &set(char);
Screen &set(index,index,char);

char get() const
{
return contents[cursor];
}
char get(index ht,index wd) const;

index get_cursor() const;

Screen &display(std::ostream &os)
{
do_display(os);
return *this;
}
const Screen &display(std::ostream &os) const
{
do_display(os);
return *this;
}

Screen():cursor(0),height(0),width(0){}
Screen(index,index,const std::string &tmp);

private:
void do_display(std::ostream &os) const
{
os << contents;
}

std::string contents;
index cursor;
index height,width;
};

inline char Screen::get(index ht,index wd) const
{
index row = width * ht;
return contents[row + wd];
}

inline Screen::index Screen::get_cursor() const
{
return cursor;
}
#endif // SCREEN_H_INCLUDED

//2. in screen.cpp
#include "screen.h"

Screen::Screen(index ht,index wd,const std::string &cntnts):height(ht),width(wd),cursor(0),contents(cntnts){}

Screen &Screen::set(char c)
{
contents[cursor] = c;
return *this;
}
Screen &Screen::set(index ht,index wd,char c)
{
index row = ht * width;
contents[row + wd] = c;
return *this;
}

Screen &Screen::move(index r,index c)
{
index row = r * width;
cursor = row + c;
return *this;
}

//3. in main.cpp
#include <iostream>
#include "screen.h"
using namespace std;

int main()
{
Screen myScreen(5,6,"aaaaa\naaaaa\naaaaa\naaaaa\naaaaa\naaaaa\n");
myScreen.move(4,0).set('#').display(cout);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: