看一道笔试题(引自程序员面试宝典):写出程序输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
  
class A
{
public:
    A() {m_a = 1; m_b = 2;}
    ~A() {}
    void fun() {printf("%d %d", m_a, m_b);}
private:
    int m_a;
    int m_b;
};
  
class B
{
public:
    B() {m_c = 3;}
    ~B() {}
    void fun() {printf("%d", m_c);}
private:
    int m_c;
};
  
void main()
{
    A a;
    B *pb = (B*)(&a);
    pb->fun();
}

程序的输出结果为1。

暂且不讨论该程序设计有多么糟糕,但程序主要考察关于类对象成员调用的机制,关于这方面,据说在《深入理解C++对象模型》中有详解,我没有做深入研究,只是根据看《primer》和《C++必知必会》中的一些知识说一下自己的理解。

这里主要涉及到两方面:一是对象调用成员函数时会将调用对象与函数绑定;二是对象访问成员是根据该成员距离对象的偏移量来访问的,而不是根据成员名来访问,所谓偏移量,就是告诉你一个特定的成员位置距离对象的起点有多少个字节。

上面程序,内存中实例化了一个A类对象,然后将该地址强制转换成一个B类地址,即将该对象的地址内容强制看成一个B类对象。pb为B类的指针,理所当然调用的是B类中的fun()函数(可以跟多态的情形相比较),当调用fun()函数时,调用对象与该函数进行绑定,即fun()函数中隐含的形参this指针初始化为调用对象(A类对象)的地址,假设为0xff80。然后fun()函数打印值m_c。这里要注意,对象在访问类成员时,编译器并没有存储该对象各个成员的实际地址,而是存储了其相对于当前对象首地址的偏移量,由于B类只有一个成员m_c,在编译阶段,编译器就记录了m_c对于B类对象的偏移量为0,故访问m_c时,便是访问当前对象地址this+偏移量0,注意,this在这里绑定的是A类对象的首地址,在A类中,偏移量为0的成员是m_a,故打印出m_a的值。

关于类成员偏移量的输出,可以用程序验证。

例如如下程序:将地址0强制转换为A类对象的地址,那么打印类成员m_a和m_b的地址便是他们的偏移量,如下分别输出0, 4。

1
2
cout<<&((A*)0)->m_a<<endl;
cout<<&((A*)0)->m_b<<endl;

再如,我们可以通过输出类成员指针来验证。

1
2
printf("%p\n",&A::m_a);
printf("%p\n",&A::m_b);

分别输出 00000000,00000004。

如程序,&A::m_a实际是一个指向int型的A类的成员指针,用m_a初始化,即相当于:

1
2
int A::*ptr = &A::m_a;

这里说一下,输出成员指针的值,最好使用printf,%p输出指针,我曾试图使用语句

1
2
cout<<&A::m_a<<&A::m_b;

结果全是1。

究其原因,应该是ostream对象没有重载类成员指针的参数,故不能直接输出类成员指针的类型,而我们知道指针类型与bool类型的转换属于标准转换的(常常用来测试指针合法性是否为空),而ostream对象可以输出bool类型,故编译器将成员指针类型转换成了bool类型,从而输出,既然这样为什么全是输出1呢?说明地址全是合法的,即偏移量全是大于0,不对呀,第一个类成员的偏移量不是0么,这里真心不明白,不过《C++必知必会》中有这样一句话:大多数编译器都将成员指针实现为一个整数,包含被指向成员的偏移量,另外加上1(加1是为了让值0可以表示一个空的数据成员指针)。这大概就是全输出1的原因了吧。

关于成员指针

成员指针只是记录一个成员的偏移量,而非地址,因为类中没有地址,选择一个类的成员只是意味着在类中偏移,只有把这个偏移和具体对象的首地址结合,才能得到实际地址。

成员指针并不指向一个具体的内存位置,它指向的是一个类的特定成员,而不是指向一个特定对象的特定成员,最直接的理解是将其理解为一个偏移量。这个偏移量适用于某一类A的任何对象,换言之,如果一个A类对象的成员a距离起点的偏移量是4,那么任何其他A类对象中,a的偏移都是4字节。

啰嗦一大堆,总结一下就是两句话:

  1. 类对象访问其成员时,是根据该成员在类中的偏移量来访问的。
  2. 类成员指针,可以理解为指向类数据成员的一个偏移量,而非地址。

成员在类中的偏移量 类成员指针相关推荐

  1. 如何在类的外部修改类中的私有数据成员

    一般的情况是类中的私有数据成员,只能是类的对象自己可以修改,从外部是不能更改的如下例子 #include<iostream> using namespace std;class Data ...

  2. PetShop 4.0知识点:base 关键字用于从派生类中访问基类的成员

    //base 关键字用于从派生类中访问基类的成员: //调用基类上已被其他方法重写的方法. //指定创建派生类实例时应调用的基类构造函数. //基类访问只能在构造函数.实例方法或实例属性访问器中进行. ...

  3. C++类中的static数据成员,static成员函数

    文章转自:http://www.cnblogs.com/gysm/archive/2011/09/16/2179277.html 版权归原作者. C++类中谈到static,我们可以在类中定义stat ...

  4. 【python】 定义一个Person类,它包含数据成员age, name和gender。从Person中派生一个类Employee,在新类中添加一个数据成员,存储个人的number.等等

    # 定义一个Person类,它包含数据成员age, name和gender. # a. 从Person中派生一个类Employee,在新类中添加一个数据成员,存储个人的number. # 再从Empl ...

  5. MFC类中获得其它类指针

    MFC类中获得其它类指针 从视图类获得文档类的指针是很容易的,用GetDocument即可,然而只能从视图类获得文档类的指针是远远不够的,每个类都有获得其它各个类指针的一套方法,本文归纳如下 AD: ...

  6. 类中定义自身类的对象

    在C++的类定义中,是不能够在类中定义自身类的对象的,但是可以定义自身类的指针对象和引用. class A { public:  A ()  {   cout<<"Constru ...

  7. 重贴:MFC类中获得其它类指针 (转)

    重贴:MFC类中获得其它类指针 (转)[@more@] MFC类中获得其它类指针 成都:苏颖锋 (vcmfc输入并转贴) 当用VC++的Application Wizard生成除了CDialog Ba ...

  8. python中自定义类中的self_Python类和构造方法

    1.面向对象介绍 描述:是函数的一个变种,在Python中,有函数式编程和面向对象编程,java和C#之类只能把函数写到类里,没有函数式编程,所以只有面向对象,所以在Python中有了选择,可能就变得 ...

  9. python中arcsec_在Python类中继承Cython类

    我有两个用cython编写的类,我想在python中的一个类中使用它们.在 位置.pyximport numpy as np cimport numpy as np cimport cython cp ...

  10. c++模板类静态成员变量_一文讲透父子类中静态变量,成员变量初始化顺序原理...

    推荐: 从面试到入职到离职,我在B站工作的30天时光 爱了爱了,Alibaba顶级MySQL调优手册到手,加薪妥了 爱了爱了,Spring Cloud Alibaba内部微服务架构笔记真的太牛了 本文 ...

最新文章

  1. Swift 使用CoreLocation获取定位与位置信息
  2. [Spring] - 读写分离
  3. 面试: String 五连杀 !你还满血吗 ?
  4. PYG教程【三】对Cora数据集进行半监督节点分类
  5. 16位汇编 写入显存B800:0000 输出字符串
  6. 《MFC游戏开发》笔记五 定时器和简单动画
  7. 如何使用ITEXTSHARP将HTML代码字符串写进PDF
  8. Visio 2003 Professional 安装序列号
  9. android 魅族手机bug多,魅族Flyme出现大面积Bug
  10. 【JS】网页自动连点器,选取网页元素连续点击
  11. 利用ffmpeg提取视频中的声音为MP3格式
  12. CentOS报错:There are no enabled repos
  13. 部署Openstack-train版
  14. information_schema.columns 学习
  15. 【Python】如何使用 Python 的 pprint库格式化和输出列表和字典
  16. HTML学习笔记 2
  17. LBS AR开发实录(1):手机位姿数据的实时获取
  18. 【Python】DS的基础学习笔记3:组合数据类型
  19. python+ tkinter做的一个项目辅助测试工具
  20. 集合框架背后的数据结构

热门文章

  1. 关于使用ajax动态输出cnzz统计代码的问题
  2. 关闭计算机休眠文件,电脑垃圾清理关闭休眠文件夹hiberfil.sys
  3. SIFT、SURF、Harris、BRIEF、FAST、DAISY、FAST经典描述子简介
  4. unity tags的坑
  5. nfc卡模式与标准模式_NFC的工作模式有哪些主动模式和被动模式的详细讲解
  6. day7_操作excel的三种方式
  7. Java练习用Java写双人游戏简单四子棋
  8. 超调量怎么计算公式_matlab超调量怎么计算 求超调量、上升时间、峰值时间、调节时间 - 西部娱乐网...
  9. 服务器虚拟化svc,服务器虚拟化与SVC技术在高校灾备中的应用
  10. 2006中国IT10大丑闻