• C++对象模型(8)-- 数据语义学:this指针


    1、this指针的认识

    this 是 C++ 中的一个关键字,也是一个 const 指针 ,它指向当前对象,通过它可以访问当前对象的所有成员。所谓当前对象,是指正在使用的对象。

    假如有这么一个类:

    1. class Base {
    2. public:
    3. int b_i;
    4. int b_j;
    5. void print() {
    6. printf(" this的地址:%p\n", this);
    7. }
    8. };

    那么这个Base类对象的this指针指向的就是变量b_i的地址。

    2、没有虚函数的情况

    this指针调整一般存在于多重继承的情况下。

    我们先进行代码演示:

    1. class X {
    2. public:
    3. int x_i;
    4. X() {
    5. cout << " X::X()的this指针是:" << this << endl;
    6. }
    7. void funcX() {
    8. cout << " X::funcX()的this指针是:" << this << endl;
    9. }
    10. };
    11. class Y {
    12. public:
    13. int y_i;
    14. Y() {
    15. cout << " Y::Y()的this指针是:" << this << endl;
    16. }
    17. void funcY() {
    18. cout << " Y::funcY()的this指针是:" << this << endl;
    19. }
    20. };
    21. class Z : public X, public Y {
    22. public:
    23. int z_i;
    24. Z() {
    25. cout << " Z::Z()的this指针是:" << this << endl;
    26. }
    27. void funcZ() {
    28. cout << " Z::funcZ()的this指针是:" << this << endl;
    29. }
    30. };
    31. int main()
    32. {
    33. Z z;
    34. z.funcX();
    35. z.funcY();
    36. z.funcZ();
    37. }

    类Z对象的内存布局是这样的:

    代码的输出结果是这样的:

    从输出结果可以画出this指针的指向是下面这样的。

    这里有2个要点:

    (1) 这里Z类先继承X、再继承Y。派生类Z的地址和第1个继承的基类X的地址是相同的。

    (2) 调用哪个子类的成员函数,这个this指针就会被编译器自动调整到对象内存布局中对应类对象的起始地址。

    3、父类没有虚函数、子类有虚函数

    1. class X {
    2. public:
    3. int x_i;
    4. X() {
    5. cout << " X::X()的this指针是:" << this << endl;
    6. }
    7. void funcX() {
    8. cout << " X::funcX()的this指针是:" << this << endl;
    9. }
    10. };
    11. class Y : public X {
    12. public:
    13. int y_i;
    14. Y() {
    15. cout << " Y::Y()的this指针是:" << this << endl;
    16. }
    17. void funcY() {
    18. cout << " Y::funcY()的this指针是:" << this << endl;
    19. }
    20. virtual void virFuncY() { }
    21. };
    22. int main()
    23. {
    24. Y y;
    25. printf(" 变量a的偏移量:%d\n", &X::x_i);
    26. printf(" 变量c的偏移量:%d\n", &Y::y_i);
    27. y.funcX();
    28. y.funcY();
    29. }

    这时输出的结果是这样的:

    从输出结果可以看到:

    (1) 父类X的this指针比子类X高4个字节,类X的变量x_i的偏移量是0。

    这个可以这么理解:虚函数表指针vptr总是在对象的头部,因为父类X没有虚函数表指针,所以父类X对象的指针要往下跳过vptr(4个字节),然后就直接访问变量x_i,所以x_i的偏移量是0。

    (2) 类Y的变量y_i的偏移量是8。因为vptr、int x_i各占4个字节,所以变量y_i的偏移量是 4 + 4 = 8。

    4、父类、子类都有虚函数

    我们给类X加上虚函数:

    virtual void virFuncX() { }

    然后看输出结果:

    从结果我们可以看到,父类X的this指针和类Y的this指针相同了,且变量x_i的偏移量变成了4。这4个字节是虚函数表指针所占用的空间。

    5、多重继承且父类都带虚函数

    我们在原来的代码上再添加1个新类Z,且让Z继承自类X、Y:

    1. class Z : public X, public Y {
    2. public:
    3. int z_i;
    4. Z() {
    5. cout << " Z::Z()的this指针是:" << this << endl;
    6. }
    7. void funcZ() {
    8. cout << " Z::funcZ()的this指针是:" << this << endl;
    9. }
    10. };
    11. int main()
    12. {
    13. Z z;
    14. printf(" 变量x_i的偏移量:%d\n", &X::x_i);
    15. printf(" 变量y_i的偏移量:%d\n", &Y::y_i);
    16. printf(" 变量z_i的偏移量:%d\n", &Z::z_i);
    17. z.funcX();
    18. z.funcY();
    19. z.funcZ();
    20. }

    输出结果:

    因为2个父类都有虚函数,所以类Z会继承这2个虚函数表指针。但2个父类X、Y是并级的,对于每一个父类对象来说,它的成员变量都会偏移4个字节。而子类的变量会偏移16个字节。

  • 相关阅读:
    希尔排序C#
    猿创征文|不会代码也能玩开发?基于华为云 IoT 快速实现 0 代码体验物联网设备上云
    驱动开发day4
    Java —— 抽象类和接口
    DHCP中继 ||| 四种通信方式(单播+组播+广播+任播)
    Java反射
    gin实现event stream
    为什么说指针是 C 语言的精髓?
    [论文阅读]Voxel R-CNN——迈向高性能基于体素的3D目标检测
    Java多线程秘籍,掌握这5种方法,让你的代码优化升级
  • 原文地址:https://blog.csdn.net/mars21/article/details/133720007