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

简介:

本节书摘来自异步社区出版社《C++面向对象高效编程(第2版)》一书中的第章,第2.16节,作者: 【美】Kayshav Dattatri,更多章节内容可以访问云栖社区“异步社区”公众号查看。

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;
}```
相关文章
|
3月前
|
Java C#
C# 面向对象编程解析:优势、类和对象、类成员详解
OOP代表面向对象编程。 过程式编程涉及编写执行数据操作的过程或方法,而面向对象编程涉及创建包含数据和方法的对象。 面向对象编程相对于过程式编程具有几个优势: OOP执行速度更快,更容易执行 OOP为程序提供了清晰的结构 OOP有助于保持C#代码DRY("不要重复自己"),并使代码更易于维护、修改和调试 OOP使得能够创建完全可重用的应用程序,编写更少的代码并减少开发时间 提示:"不要重复自己"(DRY)原则是有关减少代码重复的原则。应该提取出应用程序中常见的代码,并将其放置在单一位置并重复使用,而不是重复编写。
50 0
|
9月前
面向对象的三大特征(3)-多态
面向对象的三大特征(3)-多态
49 0
|
6月前
|
存储 算法 Java
面向对象编程实践:类、对象与继承
面向对象编程实践:类、对象与继承
34 0
|
9月前
|
存储 设计模式 算法
03-📝C++核心语法|面向对象1【 C++编程规范、类和对象、面向对象程序设计案例、对象的构造和析构、C++面向对象模型初探】
复习`C++核心语法`,且适当进行汇编探索底层实现原理,进一步夯实基础,为以后的`底层开发`、`音视频开发`、`跨平台开发`、`算法`等方向的进一步学习埋下伏笔。
03-📝C++核心语法|面向对象1【 C++编程规范、类和对象、面向对象程序设计案例、对象的构造和析构、C++面向对象模型初探】
|
9月前
|
存储 Java 开发者
Java面向对象编程三大特征 - 多态
Java面向对象编程三大特征 - 多态
75 0
|
Java 索引
java面向对象三大特征之一多态(多类调用)
多态是方法的多态,和属性没有关系
90 1
java面向对象三大特征之一多态(多类调用)
|
设计模式 C++ 容器
C++设计模式之SFINAE:用来检测类中是否有某个成员函数
针对类中特定成员函数的检测其实在工作中也可能用到。C++中可以用SFINAE技巧达到这个目的。
387 0
C++设计模式之SFINAE:用来检测类中是否有某个成员函数
先鸡后蛋-继承类实例化分析 | 带你学《Java面向对象编程》之三十七
本节结合实例讲解了继承类实例化的详细过程,并通过另一则案例验证了实例化继承类的同时必先实例化父类的说法。