C++中ECS内Vector元素的解引用指针无法更新值问题
问题场景
我在练习C++实现简易Entity Component System(ECS)框架时碰到了一个问题:ECS_Manager通过模板为每种组件类型维护独立的存储容器,组件结构体包含int类型的entity_id和Vector2D类型的position。组件的添加、获取功能看似正常,但在Physics_System里修改Position_Component的position值时,新值始终无法保存到存储容器中。
最初尝试直接赋值也没有效果:
world.get_component<Position_Component>(0)->position = Vector2D(4.4, 3.3);
排查ECS的存储逻辑未发现意外拷贝问题,最终定位到Vector2D类的运算符实现上。
相关代码
ECS核心实现
#include <iostream> #include <vector> #include <map> #include <typeinfo> #include <memory> #include <array> #include "Vector2D.hpp" #include "./ECS/components/Position_comp.hpp" class VComponentStorage { public: virtual ~VComponentStorage() = default; }; template <typename T> class ComponentStorage : public VComponentStorage{ public: ComponentStorage(){ this->storage_container_index = 0; } void add_component(T component){ this->storage_container.push_back(component); this->storage_container_index++; } T *get_component(int entity_id){ T* start_ptr = this->storage_container.data(); return &(start_ptr[entity_id]); } size_t get_component_count(){ return 1; } private: std::vector<T> storage_container; size_t storage_container_index; }; class ECS_Mananger{ public: ECS_Mananger(){ } template<typename T> void register_component(){ const char *type_name = typeid(T).name(); ComponentStorage<T> *comp_storage_ptr = new ComponentStorage<T>; this->T_to_comp_storage_Map.insert({type_name, comp_storage_ptr}); } template<typename T> void add_component(T component){ const char *type_name = typeid(T).name(); std::cout << type_name << std::endl; ComponentStorage<T>* my_ptr = static_cast<ComponentStorage<T>*>(this->T_to_comp_storage_Map[type_name]); my_ptr->add_component(component); } template<typename T> T *get_component(int entity_id){ const char *type_name = typeid(T).name(); ComponentStorage<T>* my_ptr = static_cast<ComponentStorage<T>*>(this->T_to_comp_storage_Map[type_name]); return my_ptr->get_component(entity_id); } private: std::map<std::string, VComponentStorage*> T_to_comp_storage_Map; }; void Physics_System(ECS_Mananger &world){ Position_Component* my_ptr = world.get_component<Position_Component>(0); *my_ptr = { 0, Vector2D(5.0, 2.0)}; }; int main() { ECS_Mananger my_world; my_world.register_component<Position_Component>(); Position_Component init_pos_val = {0, Vector2D(1.0, 2.0)}; my_world.add_component<Position_Component>(init_pos_val); while (1) { Physics_System(my_world); my_world.get_component<Position_Component>(0)->position.print(); } return 0; }
组件与Vector2D定义
// Position_comp.hpp #include "Vector2D.hpp" struct Position_Component { int entity_id; Vector2D position; };
// Vector2D.hpp class Vector2D { public: double x; double y; Vector2D() { this->x = 0.0; this->y = 0.0; } Vector2D(double x, double y){ this->x = x; this->y = y; } void print(){ std::cout << "(" << this->x << "," << this->y << ")" << std::endl; }; Vector2D operator+(const Vector2D& vec){ Vector2D result; result.x = this->x + vec.x; result.y = this->y + vec.y; return result; } Vector2D operator-(const Vector2D& vec){ Vector2D result; result.x = this->x - vec.x; result.y = this->y - vec.y; return result; } Vector2D operator=(const Vector2D& vec){ Vector2D result; result.x = vec.x; result.y = vec.y; return result; } }; Vector2D operator*(const double& s, Vector2D vec){ Vector2D result; result.x = s*vec.x; result.y = s*vec.y; return result; }
问题根源
Vector2D的赋值运算符重载完全错误
原Vector2D类中的operator=实现创建了一个新的Vector2D对象,复制传入值后返回,完全没有修改当前对象的成员变量:
Vector2D operator=(const Vector2D& vec){ Vector2D result; result.x = vec.x; result.y = vec.y; return result; }
这种写法违背了赋值运算符的核心语义:赋值操作应该修改左值对象本身,而不是返回一个无关的临时对象。当执行position = Vector2D(5.0,2.0)时,实际只是生成了一个临时对象然后丢弃,原position的x、y值根本没有变化。
修复方案
方法1:正确实现赋值运算符
修改Vector2D的operator=,直接修改当前对象的成员,并返回*this(符合C++赋值运算符的常规写法,支持链式赋值):
Vector2D& operator=(const Vector2D& vec){ this->x = vec.x; this->y = vec.y; return *this; }
返回值使用Vector2D&(引用)可以避免不必要的拷贝,同时满足a = b = c这类链式赋值的需求。
方法2:删除自定义赋值运算符
由于Vector2D类没有涉及动态内存管理或特殊资源,编译器自动生成的默认赋值运算符已经能正确完成成员变量的逐值复制,完全可以满足需求。直接删除自定义的operator=即可,代码会更简洁且不易出错。
内容的提问来源于stack exchange,提问作者user3047023

