【c++面向对象编程】第6篇:this指针:对象如何知道自己在调用谁?

目录

一、一个看起来"理所当然"的问题

二、this是什么?长什么样?

基本特征

成员函数中的等效写法

三、this的三个典型使用场景

场景1:区分参数和成员变量(最常见)

场景2:链式调用(返回*this)

场景3:在成员函数里传递当前对象给其他函数

四、const成员函数中的this

为什么要有const成员函数?

五、一个完整的例子:理解this的全貌

六、几个容易被问到的细节

[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("张三")时,它怎么知道要把"张三"赋给sname,而不是别的对象的?

你可能会说:"这还用问?就是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->namename在这里完全等价。大多数时候我们省略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是参数
}

很多编码规范建议:要么参数名加前缀(如_namename_),要么显式用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成员函数?

  1. 常对象只能调用const成员函数

cpp

复制代码
const Demo d;   // 常对象
d.getX();       // ✅ 可以(getX是const)
d.modify();     // ❌ 错误(modify不是const)
  1. 表达"只读"的语义 :如果函数不会改变对象,就应该标记为const,这是一种契约和文档。

  2. 能被常对象调用也能被非常对象调用: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成员函数中的thisconst 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成员变量和成员函数就是为这个设计的,它们不依附于任何具体对象。

相关推荐
2301_815279521 小时前
如何实现C++ Web 自动化测试实战:常用函数全解析与场景化应用指南
开发语言·前端·c++
lly2024061 小时前
MySQL 复制表
开发语言
Evand J1 小时前
【无人机控制例程】(3)多无人机队形变换控制代码,仿真程序。附MATLAB源代码
开发语言·matlab·无人机·控制·uav
小许同学记录成长1 小时前
基于 QxOrm 的 Qt 持久化层技术指南
开发语言·数据库·qt
csbysj20201 小时前
Python 列表(List)
开发语言
闻缺陷则喜何志丹1 小时前
【动态规划 前缀和】P7074 [CSP-J2020] 方格取数|普及+
c++·算法·前缀和·动态规划·洛谷
辞旧 lekkk9 小时前
【Qt】信号和槽
linux·开发语言·数据库·qt·学习·mysql·萌新
2zcode10 小时前
运动模糊图像复原的MATLAB仿真与优化
开发语言·matlab
袁雅倩199710 小时前
当吸尘器、筋膜枪都用上Type-C,供电方案该怎么选?浅谈PD取电芯片ECP5702的应用
c语言·开发语言·支持向量机·动态规划·推荐算法·最小二乘法·图搜索算法