C++不使用模板如何实现不同类型的类成员字段重写?
问题根源
- 对象切片:
Token类中存储的是TokenValue值类型而非指针/引用,传入子类对象构造Token时只会拷贝基类部分的内容,子类新增的成员会被直接截断,因此你访问到的永远是基类的void* value,初始为NULL所以打印出来是00000000。 - 成员隐藏:子类中定义的同名
value是独立于基类的全新成员,基类的==运算符默认访问的是基类自身的void* value,不会读取子类的成员值。
修复方案
1. 改造继承体系避免对象切片与成员隐藏
首先修改TokenValue.h,删除基类无用的void* value,新增虚析构函数避免内存泄漏,将比较逻辑改为纯虚接口由子类各自实现:
// TokenValue.h #include <string> #include <typeinfo> class TokenValue { public: virtual ~TokenValue() = default; virtual bool operator==(const TokenValue& tv) const = 0; }; class TV_Empty : public TokenValue { public: bool operator==(const TokenValue& tv) const override { return typeid(*this) == typeid(tv); } }; class TV_String : public TokenValue { public: std::string value; explicit TV_String(std::string v) : value(std::move(v)) {} bool operator==(const TokenValue& tv) const override { if (typeid(*this) != typeid(tv)) return false; const auto& other = dynamic_cast<const TV_String&>(tv); return value == other.value; } }; class TV_Int : public TokenValue { public: int value; explicit TV_Int(int v) : value(v) {} bool operator==(const TokenValue& tv) const override { if (typeid(*this) != typeid(tv)) return false; const auto& other = dynamic_cast<const TV_Int&>(tv); return value == other.value; } }; class TV_Float : public TokenValue { public: float value; explicit TV_Float(float v) : value(v) {} bool operator==(const TokenValue& tv) const override { if (typeid(*this) != typeid(tv)) return false; const auto& other = dynamic_cast<const TV_Float&>(tv); return value == other.value; } };
然后修改Token.h,将值存储改为智能指针避免对象切片:
// Token.h #include <memory> #include "TokenValue.h" class Token { public: enum class TokenType { // 所有支持的Token类型 }; TokenType type; std::unique_ptr<TokenValue> value; Token(TokenType type, std::unique_ptr<TokenValue> value) : type(type), value(std::move(value)) {} };
使用示例:
// 构造Token auto str_val = std::make_unique<TV_String>("测试文本"); Token str_token(TokenType::STRING, std::move(str_val)); // 比较两个值 auto int_val1 = std::make_unique<TV_Int>(123); auto int_val2 = std::make_unique<TV_Int>(123); bool is_equal = (*int_val1) == (*int_val2); // 结果为true
2. 更简洁的替代方案(C++17及以上)
如果你的编译环境支持C++17,可以直接用std::variant替代继承体系,没有虚函数开销也不会出现切片问题:
#include <variant> #include <string> struct TV_Empty {}; // 直接用variant存储所有可能的Token值类型 using TokenValue = std::variant<TV_Empty, std::string, int, float>; class Token { public: enum class TokenType { /* 自定义Token类型 */ }; TokenType type; TokenValue value; }; // variant原生支持==运算符,不需要自己实现比较逻辑
内容的提问来源于stack exchange,提问作者Kirk KD
相关产品推荐
相关产品推荐

