【c++面向对象编程】第6篇:this指针:对象如何知道自己在调用谁?
目录
一、一个看起来“理所当然”的问题
二、this是什么?长什么样?
基本特征
成员函数中的等效写法
三、this的三个典型使用场景
场景1:区分参数和成员变量(最常见)
场景2:链式调用(返回*this)
场景3:在成员函数里传递当前对象给其他函数
四、const成员函数中的this
为什么要有const成员函数?
五、一个完整的例子:理解this的全貌
六、几个容易被问到的细节
1. this可以被赋值吗?
2. this在静态成员函数中存在吗?
3. delete this 合法吗?
4. 空指针调用成员函数会发生什么?
七、这一篇的收获
一、一个看起来“理所当然”的问题
先看一个简单的类:
cpp
class Student { private: string name; int age; public: void setName(string n) { name = n; // 这里的name是谁的name? } };问题来了:setName函数里只有一个参数n,没有传入任何“对象”的信息。但当我们调用s.setName("张三")时,它怎么知道要把"张三"赋给s的name,而不是别的对象的?
你可能会说:“这还用问?就是s的啊!”
对,但编译器也需要知道这个规则。C++的实现方式就是:每个成员函数都隐藏了一个指向调用对象的指针,这个指针叫this。
上面的代码,编译器实际处理成类似这样:
cpp
void setName(Student* this, string n) { this->name = n; }调用s.setName("张三")时,编译器翻译成:
cpp
setName(&s, "张三");
这就是this的本质——它是对象的地址,指向“正在调用成员函数的那个对象”。
二、this是什么?长什么样?
基本特征
cpp
class Demo { public: void print() { cout << "我的地址是:" << this << endl; } }; int main() { Demo d1, d2; d1.print(); // 输出d1的地址 d2.print(); // 输出d2的地址 }核心要点:
this是一个指针,指向当前对象它的类型是
ClassName* const(指向类的常量指针)你可以在成员函数里直接使用
thisthis是编译器自动传给成员函数的,你不能手动传
成员函数中的等效写法
cpp
class Student { private: string name; public: void setName(string n) { this->name = n; // 显式使用this(通常省略) } string getName() { return this->name; // 等价于 return name; } };this->name和name在这里完全等价。大多数时候我们省略this->,但有几个场景必须显式写出来。
三、this的三个典型使用场景
场景1:区分参数和成员变量(最常见)
cpp
class Student { private: string name; public: // 参数名也叫name,和成员变量冲突了 void setName(string name) { name = name; // ❌ 这是自己赋值给自己,成员变量没改! } };上面的错误太经典了:name = name,左右都是参数name,成员变量纹丝不动。
解决方案:用this区分
cpp
void setName(string name) { this->name = name; // ✅ this->name是成员,右边的name是参数 }很多编码规范建议:要么参数名加前缀(如_name或name_),要么显式用this。我推荐后者,因为它一目了然。
场景2:链式调用(返回*this)
想实现这样的调用:
cpp
Student s; s.setName("张三").setAge(18).setScore(95);关键是把setXxx函数的返回值设成当前对象的引用,然后return *this。
cpp
class Student { private: string name; int age; double score; public: Student& setName(string name) { this->name = name; return *this; // 返回当前对象 } Student& setAge(int age) { this->age = age; return *this; } Student& setScore(double score) { this->score = score; return *this; } void print() { cout << name << ", " << age << ", " << score << endl; } }; int main() { Student s; s.setName("张三").setAge(20).setScore(95.5); s.print(); // 张三, 20, 95.5 }原理:s.setName("张三")返回s本身(引用),然后继续调用.setAge(20),以此类推。
这种风格在C++标准库里很常见(如cout << a << b << c),也叫流式接口。
场景3:在成员函数里传递当前对象给其他函数
有时候需要把当前对象传给另一个函数:
cpp
class Student; class School { public: void registerStudent(Student* s); }; class Student { public: void enroll(School& school) { school.registerStudent(this); // 把当前对象传进去 } };this就是“我自己”的指针,传出去让其他代码能够访问当前对象。
四、const成员函数中的this
你肯定见过这种写法:
cpp
class Student { private: string name; public: string getName() const { // 注意这个const return name; } };函数后面的const是什么意思?它修饰的是this指针。
没有const的成员函数:this的类型是Student* const(指向非常量的常量指针)
有const的成员函数:this的类型是const Student* const(指向常量的常量指针)
也就是说,在const成员函数内部:
不能修改任何成员变量
只能调用其他
const成员函数成员变量即便不是
const,也不能被修改
cpp
class Demo { private: int x; public: void modify() const { x = 100; // ❌ 错误!const成员函数不能修改成员 } int getX() const { return x; // ✅ 读取没问题 } };为什么要有const成员函数?
常对象只能调用const成员函数:
cpp
const Demo d; // 常对象 d.getX(); // ✅ 可以(getX是const) d.modify(); // ❌ 错误(modify不是const)
表达“只读”的语义:如果函数不会改变对象,就应该标记为
const,这是一种契约和文档。能被常对象调用也能被非常对象调用:const成员函数是“更友好”的接口。
五、一个完整的例子:理解this的全貌
cpp
#include <iostream> #include <string> using namespace std; class Counter { private: int value; public: Counter() : value(0) {} // 返回引用,实现链式调用 Counter& increment() { value++; cout << "increment: this = " << this << ", value = " << value << endl; return *this; } // 带参数的设置,演示区分同名 Counter& setValue(int value) { this->value = value; // 必须用this return *this; } // const成员函数:只能读取,不能修改 int getValue() const { // value = 100; // 这行放开会编译错误 return value; } // 非const成员函数:可以修改 void reset() { value = 0; } void print() const { cout << "Counter@" << this << " = " << value << endl; } }; int main() { Counter c1, c2; cout << "c1的地址:" << &c1 << endl; cout << "c2的地址:" << &c2 << endl; cout << endl; c1.increment().increment().setValue(10); c1.print(); c2.setValue(99).increment(); c2.print(); cout << "\n--- const对象测试 ---" << endl; const Counter c3; // 常对象 cout << c3.getValue() << endl; // ✅ getValue是const // c3.reset(); // ❌ reset不是const,编译错误 c3.print(); // ✅ print是const return 0; }输出示例(地址每次运行不同):
text
c1的地址:0x7ffc9e8a2a10 c2的地址:0x7ffc9e8a2a20 increment: this = 0x7ffc9e8a2a10, value = 1 increment: this = 0x7ffc9e8a2a10, value = 2 Counter@0x7ffc9e8a2a10 = 10 Counter@0x7ffc9e8a2a20 = 100 --- const对象测试 --- 0 Counter@0x7ffc9e8a2a30 = 0
注意:每个对象的this就是它自己的地址,和&c1相同。
六、几个容易被问到的细节
1. this可以被赋值吗?
cpp
void func() { this = nullptr; // ❌ 错误!this是const指针,不能修改 }this的类型是ClassName* const,指针本身是常量,不能指向别处。
2. this在静态成员函数中存在吗?
cpp
class Demo { public: static void func() { cout << this << endl; // ❌ 错误!静态函数没有this } };静态成员函数属于类,不属于对象,没有this指针。
3. delete this 合法吗?
cpp
class Demo { public: void suicide() { delete this; // 能编译,但极其危险 } };技术上可以,但你必须确保:
这个对象是用
new分配的析构后不能再访问任何成员
外部不能再
delete这个对象
强烈不推荐,属于奇技淫巧,几乎永远有更好的设计。
4. 空指针调用成员函数会发生什么?
cpp
Student* p = nullptr; p->setName("张三"); // 未定义行为,大概率崩溃除非调用的函数内部没有访问任何成员变量(包括通过this隐式访问),但这种写法极度危险,不要尝试。
七、这一篇的收获
你现在应该完全理解:
this是成员函数隐藏的参数,指向调用对象类型是
ClassName* const,不能修改指向用来区分同名的参数和成员变量
返回
*this可以实现链式调用const成员函数中的this是const ClassName* const,不能修改成员
💡 小作业:写一个
StringBuilder类,支持链式调用。要求:
append(const string& s)追加字符串,返回自身引用
toString()返回最终字符串使用示例:
cpp
StringBuilder sb; sb.append("Hello").append(" ").append("World"); cout << sb.toString(); // Hello World
下一篇预告:第7篇《static成员:属于类而不是对象的变量和函数》——有些东西应该是全类共享的,比如对象数量、配置参数。static成员变量和成员函数就是为这个设计的,它们不依附于任何具体对象。
