SUV
c编译器(把 C++ 的“面向对象”刮掉:剩下的就是 C、指针、和编译器的手法)

函数很“老实”。

你就把谁的地址传进去。

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 复现了。

改一点东西。

如果你想更快补齐这套能力。

你可以直接拿走这三样东西:

  1. 按「问题 → 原理 → 实现 → 调试/对齐」持续更新。

  2. 后台回复 「C++基础」 。 直接领 PDF。

  3. 用现代 C++ 手搓一个 RISC-V 模拟器

  4. 想知道 Git 背后到底在干嘛?这篇“从零实现 Git”讲透了。

  5. 每一篇。

    从现象讲到原理。

    如果你想把这套“从代码到系统”的视角慢慢补齐。

    如果你对某个话题更感兴趣。

    直接在评论区告诉我。

    写成后续的文章。

    你的每一次关注、在看和转发,都是我继续把这些坑填下去的理由。


    顶一下()     踩一下()

热门推荐

发表评论
0评