函数很“老实”。
你就把谁的地址传进去。
void inc(int* p){
++*p;
}
int x =0;
inc(&x);
谁被改。
后来我第一次写 C++。
新手总会问一句。
它怎么知道该改谁?”
因为你一旦把这个问题搞明白。
多一点底气。
C++ 的早年。
1979 年。
名字就叫 “C with Classes”。
给 C 加一层“类”的皮。
最出名的那个叫 CFront。
它把 C++ 翻译成 C。
这就逼着所有“面向对象的优雅”。
结构体。
指针。
从一开始就不神秘。
只不过编译器偷偷帮你塞了一个参数。
后来大家叫它 this 。
编译器以为你在“调用一个函数,并把对象地址顺手递过去”。
我们写一段稍微像样的代码。
struct Vec{
int x =0;
int y =0;
voidmove(int dx, int dy){
x += dx;
y += dy;
}
};
Vec v;
v.move(1,2);
编译器心里想的更像这样。
struct Vec{ int x; int y; };
voidVec_move(Vec* self, int dx, int dy){
self->x += dx;
self->y += dy;
}
Vec v;
Vec_move(&v,1,2);
&v 。
把函数和对象绑在一起。
其实都是 this->x 的省略写法。
它首先就是。
this 的类型: T* const
对。
更准确地说。
它是 T* const 。
但指针本身不能改指向。
但你不能在函数里干这种事。
struct Counter{
int x =0;
voidbad{
this = ptr;
}
};
因为 “我是谁”。
你进来就得对这个对象负责。
C++ 里的 const 。
听着像道德。
我们在成员函数后面加个 const 。
struct Vec{
int x =0;
int y =0;
intsumconst {
return x + y;
}
};
编译器做的事很硬。
从 Vec* const 。
然后一切就顺理成章了。
它不会跟你辩论“你是不是好人”。
不行。
改不了。
“ const 成员函数能不能调用非 const 成员函数”。
是类型不匹配。
你就只能调用那些承认自己接受 const Vec* 的函数。
链式调用这种写法。
新手喜欢背。
但你把它放回 this 的语境。
*this 就是“当前对象本体”。
下一次调用。
它跟这句很像。
Counter& r = c;
r.inc;
所以链式调用的要害。
是“你返回的到底是谁”。
链就会变成事故现场。
有时候你会看到这种写法。
struct Counter{
int x =0;
static inttwice(int v){
return v *2;
}
};
int n = Counter::twice(21);
但它跟“对象”没关系。
这点很像早期 CFront 的翻译风格。
“放在一个命名空间里”。
一点更老的经验:别把 this 当成永远不变的地址
this 是对象地址。
这句话够用了。
尤其是多重继承。
this 有时会被编译器“调一下”。
可能塞着不止一块基类子对象。
指针值就可能发生偏移。
编译器其实顺手给你挪了地址。
你先把这句记在心里就行。
但它也不是一句“永远等于对象起始地址”就完事的。
成员函数。
代码在别处。
你调用 obj.f 。
你调用一个函数。
这根隐形的参数。
等你接受了这件事。
就会自动变得好推导。
结尾
我写这些文章。
把工程里的底层问题讲清楚。
让你写代码更稳。
原理看懂了。
Bug 复现了。
改一点东西。
如果你想更快补齐这套能力。
你可以直接拿走这三样东西:
-
按「问题 → 原理 → 实现 → 调试/对齐」持续更新。
-
后台回复 「C++基础」 。 直接领 PDF。
-
用现代 C++ 手搓一个 RISC-V 模拟器
想知道 Git 背后到底在干嘛?这篇“从零实现 Git”讲透了。
每一篇。
从现象讲到原理。
如果你想把这套“从代码到系统”的视角慢慢补齐。
如果你对某个话题更感兴趣。
直接在评论区告诉我。
写成后续的文章。
你的每一次关注、在看和转发,都是我继续把这些坑填下去的理由。
