在C++中,如何在非指针场景下访问子类拷贝到父类的属性?
问题分析与解决方案
当前代码存在对象切片问题:Super类中的data是DataSuper的实例对象,当你在Sub的构造函数中执行data = i;(i是DataSub类型)时,只会将i中属于DataSuper的部分赋值给data,DataSub特有的成员a会被直接丢弃。此时data的实际类型就是DataSuper,根本不存在a成员,这是你无法访问data.a的核心原因。
以下是无需将data设为指针的可行解决方案:
方案1:Sub类自行维护DataSub实例
让Sub类保留传入的DataSub完整实例,直接访问自己维护的对象,避免依赖被切片的继承成员:
#include <iostream> #include <vector> using std::vector; using std::cout; class DataSuper { public: DataSuper() {} }; class DataSub : public DataSuper { public: int a; DataSub() : a(0) {} // 初始化a,避免未定义行为 }; class Super { public: DataSuper data; Super() {} }; class Sub : public Super { private: DataSub m_data; // Sub自行维护完整的DataSub实例 public: Sub(DataSub i) : m_data(i) { data = i; // 依然会切片,但不影响m_data的完整性 } void test() { cout << m_data.a << endl; // 直接访问自己维护的实例的a } }; int main(int argc, char *argv[]) { DataSub dataSub; dataSub.a = 10; // 给a赋值 Super* s = new Sub(dataSub); s->test(); // 输出10 delete(s); return 0; }
方案2:使用std::variant存储多态数据(C++17及以上)
利用std::variant作为data的类型,既能存储DataSuper也能存储DataSub,避免对象切片,同时无需使用原始指针:
#include <iostream> #include <vector> #include <variant> using std::vector; using std::cout; using std::variant; using std::get_if; class DataSuper { public: DataSuper() {} }; class DataSub : public DataSuper { public: int a; DataSub() : a(0) {} }; class Super { public: variant<DataSuper, DataSub> data; Super() {} }; class Sub : public Super { public: Sub(DataSub i) { data = i; // 完整存储DataSub,无切片 } void test() { // 判断data是否存储的是DataSub类型 if (auto subPtr = get_if<DataSub>(&data)) { cout << subPtr->a << endl; } } }; int main(int argc, char *argv[]) { DataSub dataSub; dataSub.a = 20; Super* s = new Sub(dataSub); s->test(); // 输出20 delete(s); return 0; }
方案3:重构为模板类(类型安全的优雅设计)
将Super设计为模板类,让子类指定data的具体类型,从根源避免切片问题:
#include <iostream> #include <vector> using std::vector; using std::cout; class DataSuper { public: DataSuper() {} }; class DataSub : public DataSuper { public: int a; DataSub() : a(0) {} }; // 模板类,允许指定data的类型 template<typename T = DataSuper> class Super { public: T data; Super() {} }; // Sub继承自Super<DataSub>,此时data就是DataSub类型 class Sub : public Super<DataSub> { public: Sub(DataSub i) { data = i; // 无切片,直接赋值DataSub } void test() { cout << data.a << endl; // 直接访问a } }; int main(int argc, char *argv[]) { DataSub dataSub; dataSub.a = 30; // 注意这里的指针类型需要是Sub*,或者用Super<DataSub>* Sub* s = new Sub(dataSub); s->test(); // 输出30 delete(s); return 0; }
内容的提问来源于stack exchange,提问作者Chicoscience
相关产品推荐
相关产品推荐

