指针地址是否会在特定情况下改变?派生类对象存储相关问询
关于C++指针与继承的三个问题解答
让我逐个拆解你的问题,结合C++的内存模型和继承机制来解释清楚:
指针的地址是否会在特定情况下发生改变?
首先得明确两个容易混淆的概念:指针变量自身的内存地址,和指针指向的目标地址。
- 指针变量自身的地址:在它的生命周期内是固定不变的。比如栈上的局部指针,从创建到出作用域销毁,它在栈中的位置不会变动;堆上分配的指针(比如
int** ptr = new int*;),这个ptr的地址也会一直保持到你delete它。只有当指针变量被销毁(比如局部变量离开作用域),它的地址才会失效,但这不是“改变”,而是该地址不再对应有效的变量了。 - 指针指向的目标地址:这个是可以主动或被动改变的。比如你给指针重新赋值
ptr = &another_var;,指针指向的地址就变成了另一个变量的地址;再比如用realloc重新分配内存时,原指针指向的旧内存会被释放,指针会被更新为新内存块的地址。另外还有一种被动情况:当vector这类容器扩容时,内部元素会被移动到新的内存区域,原来指向这些元素的指针就会失效(指向的还是旧地址,但该内存已经不属于容器了),这也可以理解为“指向的地址不再有效”。
存储基类指针的vector存入派生类指针,地址是否会偏移?
这个问题核心是C++继承中的基类指针指向派生类对象的地址调整规则,和vector的存储无关——vector只是原样保存指针的值而已。
- 单继承场景:派生类对象的起始地址和它内部的基类子对象起始地址完全一致。比如
class Derived : public Base {};,当你把Derived*类型的指针存入vector<Base*>时,指针的值(也就是指向的地址)不会有任何调整,直接就是派生类对象的起始地址。派生类新增的数据成员都在基类子对象的内存之后,不会影响基类指针的指向地址。 - 多重继承场景:如果派生类继承了多个基类,那么除了第一个基类子对象的起始地址和派生类对象地址相同外,其他基类子对象的地址会有偏移。比如
class Derived : public Base1, public Base2 {};,当你把Derived*转换成Base2*时,指针的值会比原Derived*的值多出sizeof(Base1)的偏移量。这时候如果把这个Base2*存入vector<Base2*>,指针的值已经是调整后的偏移地址了;如果存入vector<Base1*>,则指针值和派生类对象地址一致。
new基类数组存放派生类对象,编译器如何处理地址?
这里要分两种完全不同的情况,本质是对象切片的问题:
直接创建基类对象数组
比如你写Base* arr = new Base[3];,数组里的每个元素都是Base类型的对象。如果尝试把派生类对象赋值给数组元素(比如arr[0] = Derived();),编译器会执行对象切片:只把派生类对象中属于Base的部分拷贝到数组的Base对象中,派生类新增的成员会被直接丢弃。此时数组的起始地址就是第一个Base对象的地址,后续每个元素按sizeof(Base)的大小依次偏移,不存在“起始地址变更”的情况——因为你根本没存储完整的派生类对象。创建基类指针数组
比如Base** arr = new Base*[3];,数组里存储的是指向Base的指针。你可以把new Derived()的指针赋值给数组元素(arr[0] = new Derived();),此时每个指针指向的是完整的派生类对象。地址的处理和第二个问题一致:单继承下指针值等于派生类对象的起始地址;多重继承下如果是第二个及以后的基类指针,会有地址偏移。数组本身的起始地址是固定的,每个指针元素的地址按sizeof(Base*)的大小依次偏移。
内容的提问来源于stack exchange,提问作者asmmo
相关产品推荐
相关产品推荐

