计算类的大小:
大家觉得我们以下的代码,计算stu的大小是多大呢?
class stu { public: void test1(int a) { _a = a; } int test2() { ; } int _a; char _c; int _b; }; int main() { stu tmp; tmp.test1(10); tmp._b=10; }
内存对齐?
答案当然是 12 至于为什么大家可以去看这篇博客力的内存对齐:
可是我们这里不是还写得有函数嘛 为什么只算了成员变量的大小呢
首先大家这样想:如果我们每个对象都保存一份代码,相同的代码被保存多次,这又是何必呢?
所以有了只保存成员变量,成员函数放在公共的代码段(操作系统的角度来看,也就是内存中的代码段)调用的时候,也就是去公共代码段去找
如果是这样的类,大家觉得类的大小又会是多大呢?
1. class stu 2. { 3. public: 4. void test() {} 5. }; 6. 7. class s1 8. { 9. };
他们最终的答案都是 1 ,首先这里不可能是0,我们打印他们定义出来的对象的地址,是可以打印出来的,这里的 1 也不是用来存储有效地址的,这个是为了表示我们的对象存在过
this指针:
我们的成员函数中是存在又一个隐含的this指针的
class stu { public: stu(int a,int c,int b) { _a=a; _c=c; _b=b; } void test1() { cout<<_a<<"-"<<_c<<"-"<<_b<<endl; } private: int _a; char _c; int _b; }; int main() { stu s1(10,'A',20); stu s2(30,'B',40); s1.test1(); s2.test1(); }
这里的stu是一个构造函数,通过这样的调用来完成 s1和s2 的定义和初始化,无论是这里的构造函数,还是它下面的test1函数,他们都是存放在公共代码段的,我们调用都是同样的函数,那他是怎么样来区分到底是 s1 调用还是 s2调用呢
大家看这张图就可以看到,我们的函数都是调用的一样的,我们的上面方括号里面的内容确是不一样的,其实我们调用的时候,函数本样是这样的:
class stu { public: stu(stu* this,int a,int c,int b) { this->_a=a; this->_c=c; this->_b=b; } void test1(stu* this) { cout<<this->_a<<"-"<<this->_c<<"-"<<this->_b<<endl; } private: int _a; char _c; int _b; }; int main() { stu s1(&s1,10,'A',20); stu s2(&s2,30,'B',40); s1.test1(&s1); s2.test1(&s2); }
我们是通过传一个对象的地址,来通过地址来完成对象内容的改变与访问
不知道大家会不会有个这样的疑问,之前我学习这里就是这样理解错误了,那个时候我觉得我去改变为什么一定要有一个this指针呢,我这里不是有那些成员变量嘛,这些成员变量不都是对象自己的嘛,直接去访问赋值不就好了嘛
当然这样肯定是不正确的,我们是不能这样去实现对象的赋值与访问的,我们那些成员变量都只是声明,声明我们这个类里面是有这个变量的,真正要访问它,是必须通过定义对象,通过一个对象去访问它的,所以我们这个隐含的this指针是必不可少的
还有一个点大家需要注意:我们实参、形参的位置是不能这样显示去写的(因为这个是隐含的,是编译器去实现的),但是我们在函数的内部是可以显示的写的 我们的this指针也是被const修饰的,自身不能被改变,指向的对象是可以改变的
this指针可以为0嘛?
就这个问题大家看了这段代码就知道了:
class stu { public: stu(int a,int c,int b) { _a=a; _c=c; _b=b; } void test1() { cout<<_a<<"-"<<_c<<"-"<<_b<<endl; } void test2() { cout<<"Yes"<<endl; } private: int _a; char _c; int _b; }; int main() { stu* p =nullptr; p->test2(); }
大家觉得这段代码运行有问题嘛?
如果拿去试过就知道了,这段代码是没有问题的,可我这里的p不是空嘛,去访问空指针里面的内容,不是会运行崩溃嘛,那我这样问:我的成员函数是在对象里面的嘛,他们不是存放在公共代码段嘛,我们在编译的时候,如果函数的篇幅较长,这里的调用就会转换成 去 call test2 的指令了,较短的话,就是直接展开了(因为内联) 就不存在访问空指针的问题了
当然如果这里的成员函数是公有的,访问他们就不行了,这里的 test2函数 依旧也是存在隐含的this指针的,这里的this指针就是空了(我们这里也是把p的地址传过去了的)
最后给大家提一个东西,我们的this指针按理来讲是存在栈上面的,但通常编译器优化后会放到寄存器里面,上面那图,call指令上面的指令就是,把 p 存到了 ecx 寄存器里面去了