目录
[1. this可以被赋值吗?](#1. this可以被赋值吗?)
[2. this在静态成员函数中存在吗?](#2. this在静态成员函数中存在吗?)
[3. delete this 合法吗?](#3. delete this 合法吗?)
[4. 空指针调用成员函数会发生什么?](#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(指向类的常量指针) -
你可以在成员函数里直接使用
this -
this是编译器自动传给成员函数的,你不能手动传
成员函数中的等效写法
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成员变量和成员函数就是为这个设计的,它们不依附于任何具体对象。