C语言面向对象继承实现别名问题:强转子类为父类指针取值错误
问题根源
你对C标准6.7.2.1.13条款的适用场景理解有误,该条款的生效前提是:结构体的首成员是父类的实例,而非父类的指针。
你当前定义的struct Rectangle首成员是Shape*类型的指针,存储的是Shape实例的内存地址,你将Rectangle*强转为Shape*后,Shape_getX/Shape_getY会把Rectangle首地址处存储的指针值当做Shape的x/y成员读取,自然得到错误结果。
修复方案
根据你要隐藏结构体实现的需求,有两种成熟的实现方案可选:
方案1:调整结构体布局,支持直接强转
将Rectangle的首成员改为Shape类型的实例(而非指针),即可符合C标准的规则,强转后读取的值完全正确:
// rectangle.c 中修改结构体定义 struct Rectangle { Shape super; // 改为Shape实例,不是指针 unsigned int width; unsigned int height; }; // 构造函数对应修改 Rectangle* Rectangle_ctor(int x, int y, unsigned int width, unsigned int height) { Rectangle* me = malloc(sizeof(struct Rectangle)); // 直接初始化首成员的Shape实例,无需单独malloc me->super.x = x; me->super.y = y; me->width = width; me->height = height; printf("x: %d\n", Shape_getX(&me->super)); // 正确 printf("y: %d\n", Shape_getY(&me->super)); // 正确 printf("x: %d\n", Shape_getX((Shape*)me)); // 现在取值正确 printf("y: %d\n", Shape_getY((Shape*)me)); // 现在取值正确 return me; }
注意:该方案需要将
struct Shape的定义放到公共头文件shape.h中,否则Rectangle.c编译时无法获取Shape的内存布局。
方案2:保留指针实现,提供显式转换函数
如果你希望完全隐藏Shape和Rectangle的结构体实现,不需要依赖强转,只需对外提供显式转换函数即可:
- 在
rectangle.h中新增转换函数声明:
// 新增到rectangle.h Shape* Rectangle_asShape(Rectangle* me);
- 在
rectangle.c中实现转换函数:
Shape* Rectangle_asShape(Rectangle* me) { return me ? me->super : NULL; }
- 客户端直接调用转换函数即可,无需感知内部实现:
// main.c 中用法 int main(void) { Rectangle* r1 = Rectangle_ctor(0, 2, 10, 15); printf("r1: (x=%d, y=%d, width=%d, height=%d)\n", Shape_getX(Rectangle_asShape(r1)) , Shape_getY(Rectangle_asShape(r1)) , Rectangle_getWidth(r1) , Rectangle_getHeight(r1)); return 0; }
该方案完全隔离了实现细节,后续就算调整结构体内部布局,只要同步修改转换函数的实现,客户端代码无需任何改动,兼容性更强。
内容的提问来源于stack exchange,提问作者Mr.nerd3345678
相关产品推荐
相关产品推荐

