派生类中成员字段别名(无访问器函数)实现及示例探讨
在C++派生类中实现成员字段别名(无需访问器函数)
要在C++派生类里给基类元素设置别名,又不想写冗余的访问器函数?咱们可以用几种安全且直观的方式实现,下面就针对你给出的static_vector和vector3的场景逐一说明:
方法一:引用成员变量(最直观安全)
在派生类里声明引用成员,并在构造函数中把它们绑定到基类的对应元素上,这样就能像访问普通成员一样直接用x/y/z读写基类数据:
#include <array> template<typename T, std::size_t N> class static_vector { public: T& operator[](std::size_t i) { return m_elements[i]; } T const& operator[](std::size_t i) const { return m_elements[i]; } private: std::array<T, N> m_elements; }; template<typename T> class vector3 : public static_vector<T, 3> { public: using vector_type = static_vector<T, 3>; // 把x/y/z声明为引用成员,作为基类元素的别名 T& x; T& y; T& z; // 必须在构造函数初始化列表中绑定引用 vector3() : x(vector_type::operator[](0)), y(vector_type::operator[](1)), z(vector_type::operator[](2)) {} // 带初始值的构造函数示例 vector3(T x_val, T y_val, T z_val) : vector3() { x = x_val; y = y_val; z = z_val; } };
优点:用法完全和普通成员变量一致,代码可读性高,完全符合C++标准,没有未定义行为;
缺点:派生类会额外占用3个引用的存储空间(通常是每个引用占一个指针大小,开销很小),且必须显式定义构造函数完成引用初始化。
方法二:利用Union实现零开销内存别名
如果追求零额外内存开销,可以用union让基类的数组和x/y/z共享同一块内存空间。不过需要注意基类的成员访问权限,以及内存布局的兼容性:
#include <array> template<typename T, std::size_t N> class static_vector { public: T& operator[](std::size_t i) { return m_elements[i]; } T const& operator[](std::size_t i) const { return m_elements[i]; } protected: // 把m_elements改为protected,让派生类的union能访问 std::array<T, N> m_elements; }; template<typename T> class vector3 : public static_vector<T, 3> { public: using vector_type = static_vector<T, 3>; // 通过union实现内存共享,x/y/z和基类数组指向同一块内存 union { std::array<T, 3>& elements = vector_type::m_elements; struct { T x, y, z; }; }; // 构造函数可以直接使用默认实现 vector3() = default; vector3(T x_val, T y_val, T z_val) : x(x_val), y(y_val), z(z_val) {} };
优点:完全没有额外内存开销,x/y/z直接映射基类数组的内存;
注意事项:必须确保std::array<T,3>和struct {T x,y,z;}的内存布局完全一致(对于基本类型、标准布局类型来说,C++标准会保证这一点),同时基类的数组成员需要设为protected或public。
方法三:类型别名+指针强制转换(慎用)
如果不想修改基类的访问权限,也可以通过指针强制转换直接映射内存,但这种方式依赖内存布局,风险较高,仅适合对内存布局有明确把握的场景:
#include <array> template<typename T, std::size_t N> class static_vector { public: T& operator[](std::size_t i) { return m_elements[i]; } T const& operator[](std::size_t i) const { return m_elements[i]; } private: std::array<T, N> m_elements; }; // 定义和vector3内存布局匹配的结构体 template<typename T> struct vector3_data { T x, y, z; }; template<typename T> class vector3 : public static_vector<T, 3> { public: using vector_type = static_vector<T, 3>; // C++17及以后可以用inline引用直接作为别名 inline T& x = reinterpret_cast<vector3_data<T>*>(this)->x; inline T& y = reinterpret_cast<vector3_data<T>*>(this)->y; inline T& z = reinterpret_cast<vector3_data<T>*>(this)->z; };
注意:这种方式要求static_vector的第一个成员必须是m_elements,且std::array<T,3>的内存布局和vector3_data<T>完全一致,否则会触发未定义行为,所以除非必要,不推荐使用。
内容的提问来源于stack exchange,提问作者0xbadf00d
相关产品推荐
相关产品推荐

