BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / cpp / #95087同步于 2017/4/14
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖

【问题】对象所占的内存空间是否含有"类型"的信息?

liuyehcf
2017/4/14镜像同步19 回复
1、指针类型可以在编译期强制转换为任何其他指针类型(运行时是否报错先不管)。是不是仅通过指针的类型来获取该内存的一种视图(或者说获取该内存使用方式),而内存本身并没有存放有关类型的信息? 2、C++在为对象分配内存空间时会有一些额外开销(overhead),这些额外的内存存储了哪些与对象无关的东西?
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
inaadversity机器人#1 · 2017/4/14
1、是的 2、实例化对象时,默认并没有额外开销,当然如果你为某个类实现了new操作符重载,那么你可以做任何事。
wk1948机器人#2 · 2017/4/14
额,这种问题要去看汇编代码。 编译过后,所谓指针其实就是一个地址(可能已经没有相对应的东西了),对于基本类型,内存里面是没有类型信息的。语言的底层实现应该并没有相应的标准,所以不同的编译器的汇编代码也不一样。
liuyehcf机器人#3 · 2017/4/14
非常感谢~~~按照你的说法重载了一下operator new 和operator delete ```C class A { char c[4]; public: void * operator new(size_t size); void operator delete(void *); }; void * A::operator new(size_t size) { void *raw = (void*)malloc(size+2); char* pc = (char*)raw; pc[0] = 'a'; pc[1] = 'b'; return (void*)(pc+2); } void A::operator delete(void *p) { char * pc = (char*)p; cout << *(pc - 2) << endl; cout << *(pc - 1) << endl; free((void*)(pc - 2)); } int main() { cout << sizeof(A) << endl; A *a = new A(); delete a; system("pause"); } ``` 输出: 4 a b 【 在 inaadversity 的大作中提到: 】 : 1、是的 : 2、实例化对象时,默认并没有额外开销,当然如果你为某个类实现了new操作符重载,那么你可以做任何事。
liuyehcf机器人#4 · 2017/4/14
谢谢解惑~,可是汇编一点不会啊,是时候学习一波了 【 在 wk1948 的大作中提到: 】 : 额,这种问题要去看汇编代码。 : 编译过后,所谓指针其实就是一个地址(可能已经没有相对应的东西了),对于基本类型,内存里面是没有类型信息的。语言的底层实现应该并没有相应的标准,所以不同的编译器的汇编代码也不一样。
bond1993机器人#5 · 2017/4/14
1、是的 2、开销包括时间和系统记录堆区情况的存储空间
nuanyangyang机器人#6 · 2017/4/14
这个话题叫run-time type information (RTTI) 理论上,c和c++的每个存储空间(不管是在堆上还是栈上还是别的什么位置),都有一个类型这块空间只能存这个类型的值。但c语言允许程序员把任何指针转换成void*型,然后在使用之前转换回原来的类型。只要指针实际指向的存储空间是期望的类型,就能合法地访问。这样做,c可以让不同目标类型的指针用相同的方法存储(比如在指针数组里),也可以写一个能够接受任何指针类型参数的函数(对于回调函数很有用),但也使得c成为弱类型语言:指针转换的时候一旦猜错了,访问的时候就是未定义行为。cpp继承了c的这个特性,也允许指针转换。 但是,如果问“给定一个void*型的指针,怎么在运行时知道它指向的空间实际是什么类型的?”,就是完全不同的问题了。这个过程发生在运行时,必须通过查询元数据来完成。这就和语言的实现相关了。语言的实现(包括编译器和运行库)就要在存储空间的附近(或者别的什么能通过地址找到的地方)存放一些标签,来标识这块存储空间的类型。 从接口上来看,c完全没有这种接口,程序员必须自己实现这个机制(比如自己在每个需要查询的空间开头放个标签)。cpp的话,标准库里的typeinfo库提供了这种接口。java、python这种有虚拟机的语言一般提供更高级的反射机制。
nuanyangyang机器人#7 · 2017/4/14
具体地说,http://en.cppreference.com/w/cpp/language/typeid typeid(expr)这个表达式,如果expr是多态类型的(至少有一个虚成员函数的类)就会返回运行时这个表达式的对象的实际类型的type_info。如果不是多态类型的,就返回expr编译时的静态类型的type_info。 所以,如果编译时expr是基类类型,运行时也可能得到派生类类型的type_info。 所以,对于cpp来说,多态类型的实例是需要藏一些元数据的,但非多态类型就不需要了。
nuanyangyang机器人#8 · 2017/4/14
再回答原po的问题。 1:是不是能把内存看成没有类型的,只把编程语言里的类型看成一种视图? 我的观点是:如果用汇编语言,是的。内存就是没有类型的,都是字节,指令能够按字节或者按字操作内存。 如果用java、python这样的高级语言,不能。这种语言完全将内存抽象了,存储空间都是强类型的,不能当一串字节来“重新解读”,甚至垃圾回收器把“gc的时候会整理内存碎片,把存储空间挪来挪去”这个事实都可以隐藏起来。 如果用c和c++,那么……语言的设计者只给出了“像用java和python那样写的c的程序”的语义,把那种“将内存当成字节串重新解读”定义成“实现相关的行为”。但是可以看出c语言标准的制定者是期望用户在一定情况下这样用的(这样很危险,而且不能移植),而且很多程序员真的这样干。
nuanyangyang机器人#9 · 2017/4/14
2、cpp藏了哪些信息? 只要能实现typeinfo库里的接口,实现dynamic_cast,以及其它cpp标准要求的用户可见的信息,就可以了。 实现java需要维护的信息或许更多。