《C++面向对象高效编程(第2版)》——2.16 识别成员函数的目标对象

2.16 识别成员函数的目标对象

C++面向对象高效编程(第2版)
在编写成员函数(构造函数、析构函数、操作符等)的代码时,如何显式表示调用该成员函数的对象?或者,如果需要,如何显式返回目标对象(target object)的值?在成员函数内部,如何访问调用该成员函数的对象中的数据成员?

这就是this指针发挥作用的地方。类的每个成员函数都有一个特殊的指针——this。这个this指针内含调用成员函数的对象的地址(即this指针总是指向目标对象)。this指针只在成员函数内部有效,this是C++中的关键字。

this指针的类型是“指向成员函数所属类的指针”,也可以说“this的类型是类名”。在成员函数内部,this指针指向调用该成员函数的类实例。

编译器对待成员函数并没有什么特别。实际上,编译器就像实现普通函数那样实现成员函数,但是,它会专门对成员函数进行名称重整(name mangling)以确保其唯一性。每个成员函数接受的第一个参数就是this指针。尽管程序员从未显式声明this指针,但是它一定存在。this指针通常是每个(非静态)成员函数隐含的第一个参数,编译器在每个成员函数的声明中都会插入这个隐含的参数。为了说明这个概念,显式声明this指针如下,Print()成员函数应是:

void TInt::Print(const TInt* this)
{
  cout << "0x" << _mostSignificantPart << ",0x" << _leastSignificantPart;
}```
实际上,this指针的声明在已重整函数名(mangled function name)中可见。因此,TInt::Print应该是:

void Print_3TIntFv(const TInt* this)
{
  cout << "0x" << this->_mostSignificantPart << ", 0x" <<
          this->_leastSignificantPart;
}`
一旦离开成员函数,this名称将不再有效。

hand是否一定要使用this指针来引用目标对象中的成员?

不是所有情况都需要这样做。只有在成员函数使用该类成员(数据成员或成员函数)的非限定(unqualified name)名时,才意味着使用this指针。如果在成员函数内部引用类的成员,编译器会在每条表达式中均插入this指针(如果用户没有这样做)。回顾Print()函数,可以这样改写:

void TInt::Print()
{
 cout << "0x" << this->_mostSignificantPart << ", 0x" <<
     _leastSignificantPart;
}```
`this- >_mostSignificantPart`表达式使用this指针显式访问数据成员`_mostSignificantPart`。`this- >_mostSignificantPart`表达式的意思是:`this`指针指向该对象中的 _`mostSignificantPart`数据成员。this指针只是成员函数的一个参数(但存在一些限制,将在其他地方讨论),可以像使用成员函数的其他参数那样使用this指针。甚至在2.15节的`Print()`实现中(没有显式使用this指针引用成员),编译器也会将_mostSignificantPart表达式自动展开为this- >_mostSignificantPart表达式。

在如下代码段中,

TInt aInt;
aInt.Print();`
对象aInt调用Print()(即向对象aInt发送Print()消息)。在Print()函数中this指针将指向aInt。

由于this是指向对象的指针,因此,如果要使用this指针获得整个对象,我们必须使用操作符对this指针解引用(de-reference)为*this。正如其他指针那样,this内部存放的是对象的地址,this则是该对象的值。

this指针的概念非C++独享。OOP语言在涉及接收消息的对象时,使用不同的名称。如Smalltalk称为Self,Eiffel称之为Current

C++:

现在,把我们的注意力转到TInt类的一些操作符函数上。

 // +操作符的实现
TInt TInt::operator+(const TInt& operand) const
 // TInt 是该操作符函数的返回类型
{
 /*
   用于计算操作数和TInt数之和的代码,TInt数调用+操作符函数,this指针指向TInt 数。该函数计算*this和操作数之和,并将计算结果以新的TInt数返回,未修改*this或操作数(因此用const限定符)。算法如下:
    1.加上 _leastSignificantPart部分并保存进位位元(carry bit)
    2.使用进位位元加上 _mostSignificantPart部分
    3.把(1)和(2)储存在临时TInt数中
    4.按值方式返回临时TInt数
 */
 TInt result = *this; // ① 调用复制构造函数
 unsigned char carry = 0;
  // 加上 _leastSignificant部分并检查进位
 result._leastSignificantPart += operand.GetLeastSignificantPart();
 if ( result._leastSignificantPart < operand.GetLeastSignificantPart() )
  carry = 1;
  // 带进位加上 _mostSignificant
 result._mostSignificantPart += carry + operand.GetMostSignificantPart();
 return (result);
}
 // 构造函数的框架
 TInt::TInt(long msp, unsigned long lsp)
 {
     // 将传递给构造函数的值复制至相应的数据成员中
   _leastSignificantPart = lsp;
   _mostSignificantPart = msp;
}```
时间: 2024-09-20 11:44:30

《C++面向对象高效编程(第2版)》——2.16 识别成员函数的目标对象的相关文章

《C++面向对象高效编程(第2版)》——导读

前言 C++面向对象高效编程(第2版) 面向对象软件开发已逐渐成为开发软件的首选.优秀的面向对象软件开发人员.设计人员.系统架构师对其需求与日俱增.要想成为一名成功的面向对象编程(OOP)人员必须忘却(摈弃)多年来面向程序编程的习惯,从新的角度分析问题. 面向对象编程要求程序员和设计者非常熟悉一些基本范式或概念.理解这些范式是在面向对象软件领域打下牢固基础的基本要求.支持OOP的语言都必须支持这些基本范式.换言之,学习OOP,简单地说,就是学习许多语言(如C++,Eiffel,SmallTalk

《C++面向对象高效编程(第2版)》——4.2 无用单元收集问题

4.2 无用单元收集问题 C++面向对象高效编程(第2版) 在我们讨论无用单元收集1(garbage collection)之前,先了解一下何为无用单元(garbage),何为悬挂引用(dangling reference). 4.2.1 无用单元 所谓无用单元(garbage),是一块存储区(或资源),该存储区虽然是程序(或进程)的一部分,但是在程序中却不可再对其引用.按照C++的规定,我们可以说,无用单元是程序中没有指针指向的某些资源.以下是一个示例: main() { char* p =

《C++面向对象高效编程(第2版)》——4.6 对象赋值的语义

4.6 对象赋值的语义 C++面向对象高效编程(第2版) 赋值与复制的操作非常类似.在C++中,绝大多数的复制操作都由语言隐式调用(当对象按值传递或按值返回时).当通过现有对象创建新对象时,也进行了复制操作(但不是很频繁).与复制相反的是,赋值是必须由程序员显式调用的操作.然而,在Eiffel和Smalltalk中,赋值和复制操作都由程序员显式调用.这也是基于值的语言与基于引用的语言之间的区别. 在C++中,对于对象和基本类型赋值都具有相同的含义.把基本类型变量赋值给另一个(兼容的)基本类型变量

《C++面向对象高效编程(第2版)》——2.21 确保抽象的可靠性——类不变式和断言

2.21 确保抽象的可靠性--类不变式和断言 C++面向对象高效编程(第2版) 任何抽象都必须与客户履行它的契约(contract).当客户使用类时,他希望类的对象像其发布描述的那样运行正常.另一方面,类的实现者必须千方百计地确保对象运行正常.但是,类只有在客户履行自己那部分契约后,才能正确行使它的职责.例如,类的成员函数可能要求传入的参数为非零指针(non-zero pointer).只有满足此前提条件,成员函数才能保证它的行为.因此,客户必须履行一些义务.换言之,如果客户履行了她那部分契约,

《C++面向对象高效编程(第2版)》——2.30 has-a关系的重要性

2.30 has-a关系的重要性 C++面向对象高效编程(第2版) "has-a"关系(也称为关联.聚集.包含.组合)是在OOD(面向对象设计)中频繁使用的重要关系.但是,许多设计者和程序员都没有很好地理解其相关性,从而导致复杂僵化的设计和不必要的继承. 在OOD阶段,软件的复用主要通过两种方式完成:继承和包含.它们各有优缺点.优秀的设计者了解它们的局限性.优点和代价,可以灵活自如地应用它们.继承将在第5章.第6章以及第二部分的第12章中详细讨论. 包含是一项强大的设计技术,它比继承更

《C++面向对象高效编程(第2版)》——2.27 关联

2.27 关联 C++面向对象高效编程(第2版) 关联表示对象与不同类之间的结构关系(structual relationship),大多数关联都是二元关系(binary relation).类之间的多重关联(multiple association)和类本身的自关联(self association)都是合法的(见图2-19). 关联可以有一个名称,表明阅读方向的箭头为可选.注意,方向箭头为可选,但关联名必须显示.关联在不同的方向可以有不同的名称,但是,大多数情况下,没必要注明(特别是在已标出

《C++面向对象高效编程(第2版)》——3.2 类要素的细节

3.2 类要素的细节 C++面向对象高效编程(第2版) 3.2.1 访问区域 客户可以访问在类的public区域中声明的任何成员.我们可以把该区域看做是通用公共(general public)的接口,它没有任何保护,是类限制最少的区域.一个设计良好的类绝不会将数据成员包含在public区域,该区域只能包含成员函数.如果在public区域包含数据成员,那么无需类的实现者,仅通过编译器即可访问这些数据成员.这违反了数据抽象和封装原则.这也是我们为什么总将数据成员放在private或protected

《C++面向对象高效编程(第2版)》——第2章 什么是数据抽象

第2章 什么是数据抽象 C++面向对象高效编程(第2版) 面向对象编程的一项基本任务是创建带有适当功能的类,并隐藏不必要的细节(即抽象数据).下面,我们将用一个现实生活中的例子来解释数据抽象的概念. 绝大多数人都见过影碟播放机(laser disc player)(或LD播放机).现在,提出一个简单的问题:设计一个影碟播放机,要求易于使用和修改,可后续添加更多有用的功能. 注意: 如果难以理解影碟播放机,可以用CD播放机代替LD播放机,其设计原理类似.实际上,影碟播放机的功能是CD播放机功能的超

《C++面向对象高效编程(第2版)》——1.5 什么可以作为类

1.5 什么可以作为类 C++面向对象高效编程(第2版) 用简单的例子详细讨论类和对象非常容易,但是难点在于如何为给定的问题找出合适的类.我们必须理解类代表什么,何时将问题中的某些部分转化为类,而非数据,反之亦然.根据我们的定义,类拥有一组对象的共同属性(或者特性).怎样的共同才是共同?何时说这是一个类,而不是另一个类的对象?这些都是我们在学习OOP时会遇到的,和真正关心的问题. 当我们决定创建一个类时,第一个问题就是"是否确实需要这个类的多个实例?",如果答案为"是&quo