如何在C++派生类中初始化基类的Detail结构体成员
问题描述
现有如下C++代码:
struct Detail{ int cost; int age; bool danger; unsigned char country[3]; }; class Toy{ public: Toy(); virtual ~Toy(); };
可以在派生类(如doll)中直接初始化Detail结构体:
class doll: public Toy { public: doll(); virtual ~doll(); Detail detail={4,12,false,"CN"}; };
但因为存在多个派生类(如Leggo),它们的Detail结构体结构相同但值不同,希望将Detail结构体放到基类Toy中,这样设计更合理。请问如何在派生类中初始化基类中的该结构体?
另外需要将派生类对象指针存入基类指针容器:
std::vector<Toy*> toy_list; doll* one = new doll(); toy_list.push_back(one); Leggo* two = new Leggo(); // 另一个继承自Toy的类 toy_list.push_back(two);
最终通过容器访问每个对象的结构体成员:
for(auto obj: toy_list) { printf(" toy is from %s \n", obj->detail.country); };
解决方案
方法1:基类提供带参构造函数,派生类通过初始化列表传递参数
先修改基类Toy,把Detail作为成员变量,同时添加接受Detail参数的构造函数:
struct Detail{ int cost; int age; bool danger; unsigned char country[3]; }; class Toy{ public: // 带参数的构造函数,用于初始化Detail成员 Toy(const Detail& d) : detail(d) {} virtual ~Toy() = default; Detail detail; // 基类中的Detail成员 };
派生类在构造函数的初始化列表中调用基类构造函数,传入对应Detail值:
class doll: public Toy { public: // 直接传递初始化好的Detail对象 doll() : Toy({4, 12, false, {'C', 'N', '\0'}}) {} virtual ~doll() = default; }; class Leggo: public Toy { public: Leggo() : Toy({10, 6, true, {'U', 'S', '\0'}}) {} virtual ~Leggo() = default; };
注意:country是长度为3的数组,必须手动添加字符串终止符'\0',否则printf的%s会读取到内存中的无效数据。
方法2:基类提供初始化接口,派生类构造时调用
如果基类需要保留默认构造函数,可以给基类添加一个初始化Detail的成员函数:
class Toy{ public: Toy() = default; virtual ~Toy() = default; // 初始化Detail的接口函数 void initDetail(const Detail& d) { detail = d; } Detail detail; };
派生类构造函数中调用该接口完成初始化:
class doll: public Toy { public: doll() { initDetail({4, 12, false, {'C', 'N', '\0'}}); } virtual ~doll() = default; };
方法3:用虚函数返回Detail(更符合多态设计)
如果想贴合面向对象的封装和多态特性,可以让基类定义纯虚函数返回Detail,派生类实现该函数,同时基类无需直接持有Detail成员:
class Toy{ public: virtual ~Toy() = default; // 纯虚函数,派生类必须实现 virtual const Detail& getDetail() const = 0; }; class doll: public Toy { private: Detail detail = {4, 12, false, {'C', 'N', '\0'}}; public: doll() = default; virtual ~doll() = default; // 重写基类的纯虚函数 const Detail& getDetail() const override { return detail; } };
此时容器遍历的代码需要调整为:
for(auto obj: toy_list) { printf(" toy is from %s \n", obj->getDetail().country); };
这种方式避免直接暴露成员变量,更符合封装原则。
内容的提问来源于stack exchange,提问作者user1538798
相关产品推荐
相关产品推荐

