TestClass* p = new TestClass();
这样可以动态创建一个C++的类对象。
这个人人都会,然而这简单的1行代码底层到底是如何创建这个类对象的呢?
今天这个视频我要带着你透视C++类的底层原理。不管你是新手还是老手,都建议你看一看,这可以让你知其然知其所以然。

示例类定义
class TestClass {
public:
TestClass(int n):memberN(n) {
}
~TestClass() {
}
void Bark() {
std::cout << "Wang Wang" << std::endl;
}
private:
int memberN;
};
这是一个简单的C++类,有一个构造函数,有一个析构函数,还有一个Bark的成员函数。
动态创建对象
TestClass* p = new TestClass(1);
p->Bark();
现在我们在这里设置断点,然后运行这个程序,再进入汇编模式。要想理解底层原理,是离不开汇编代码的。
new 操作符的汇编实现
TestClass* p = new TestClass(1);
00242820 push 4
00242822 call operator new (0241145h)
00242827 add esp,4
0024282A mov dword ptr [ebp-0FCh],eax
00242830 mov byte ptr [ebp-4],1
00242834 cmp dword ptr [ebp-0FCh],0
0024283B je __$EncStackInitStart+78h (0242852h)
0024283D push 1
0024283F mov ecx,dword ptr [ebp-0FCh]
00242845 call TestClass::TestClass (02414DDh)
0024284A mov dword ptr [ebp-110h],eax
00242850 jmp __$EncStackInitStart+82h (024285Ch)
00242852 mov dword ptr [ebp-110h],0
0024285C mov eax,dword ptr [ebp-110h]
00242862 mov dword ptr [ebp-0F0h],eax
00242868 mov byte ptr [ebp-4],0
0024286C mov ecx,dword ptr [ebp-0F0h]
00242872 mov dword ptr [p],ecx
首先将参数4压栈,它用于接下来调用new申请4个字节的内存。因为我们这个类中只有一个int型成员变量,所以它只占用4个字节的长度。
new操作符调用完成后申请的内存会保存在eax寄存器中,这里是将eax也就是申请的内存保存到局部变量中。
接着比较申请的内存地址是否为空,如果为空,就是申请内存失败了,那么就跳到错误处理。
接下来把参数1压栈,它是传递给构造函数的参数。然后把前边申请的内存加载到ecx寄存器中做为参数传递给构造函数。
关键点:this指针
请注意,虽然我们的代码中,这个类对象只有一个参数,但是从汇编代码中可以看出,它向类的构造函数传递了2个参数。而第一个参数就是申请的内存,其实就是类的this指针。
划重点:
在C++中,类的this指针总是作为第一个隐式参数传递给成员函数,包括构造函数和析构函数。
构造函数内部执行
然后进入到类的构造函数中,这几行是栈帧的处理,我们以前的视频中讲过,忘记的同学可以再翻回之前的视频。
然后将未初始化的内存赋值为CC,再把THIS指针存储到当前对象的地址。后边是检查调试器。然后就是将传进来的参数n的值赋给成员变量memberN。
成员函数调用
p->Bark();
00242875 mov ecx,dword ptr [p]
00242878 call TestClass::Bark (02410C8h)
怎么样,关于C++的类对象创建过程,你现在明白了吗?
通过这一行看似简单的 new 代码,我们看到了内存申请、this指针传递、构造函数调用等完整的底层流程,这也是C++面向对象特性的核心实现机制。


