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

简介:

本节书摘来自异步社区出版社《C++面向对象高效编程(第2版)》一书中的第2章,第2.26节,作者: 【美】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;
}```
相关文章
|
6月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
191 0
|
8月前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
325 12
|
9月前
|
安全 C++
【c++】继承(继承的定义格式、赋值兼容转换、多继承、派生类默认成员函数规则、继承与友元、继承与静态成员)
本文深入探讨了C++中的继承机制,作为面向对象编程(OOP)的核心特性之一。继承通过允许派生类扩展基类的属性和方法,极大促进了代码复用,增强了代码的可维护性和可扩展性。文章详细介绍了继承的基本概念、定义格式、继承方式(public、protected、private)、赋值兼容转换、作用域问题、默认成员函数规则、继承与友元、静态成员、多继承及菱形继承问题,并对比了继承与组合的优缺点。最后总结指出,虽然继承提高了代码灵活性和复用率,但也带来了耦合度高的问题,建议在“has-a”和“is-a”关系同时存在时优先使用组合。
491 6
|
11月前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
247 19
|
11月前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
448 13
|
11月前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
363 5
|
11月前
|
存储 C++
【C++面向对象——输入输出流】处理二进制文件(头歌实践教学平台习题)【合集】
本任务要求使用C++读取二进制文件并在每行前添加行号后输出到控制台。主要内容包括: 1. **任务描述**:用二进制方式打开指定文件,为每一行添加行号并输出。 2. **相关知识**: - 流类库中常用的类及其成员函数(如`iostream`、`fstream`等)。 - 标准输入输出及格式控制(如`cin`、`cout`和`iomanip`中的格式化函数)。 - 文件的应用方法(文本文件和二进制文件的读写操作)。 3. **编程要求**:编写程序,通过命令行参数传递文件名,使用`getline`读取数据并用`cout`输出带行号的内容。 4. **实验步骤**:参考实验指
308 5
|
10月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
6月前
|
存储 编译器 程序员
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
281 0
|
9月前
|
设计模式 安全 C++
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
189 16