【发布时间】:2020-06-21 22:25:22
【问题描述】:
我相信这将是我对该网站的第一个问题,因此对于本文中的任何错误或错误,我深表歉意。我也是一个初学者 C++ 程序员,如果我的问题被认为是“noobish”,请原谅我。
背景:父实体对象的集合在启动时创建(目前在运行时未删除或添加),然后链接到一系列激活器实体对象(在开始时和运行时期间)通过子实体对象。建立链接时,Parent 生成一个 Child(存储在本地向量中),并返回指向 Child 的指针供 Activator 存储。
激活器将“激活”与它们链接的子级,然后它们将根据内部和父级设置执行工作。在被激活后,它们也会由 Parent 定期更新,一直持续到最终停用。
下面是现有类的简化示例。
class ParentEntity {
std::vector<ChildEntity> m_Children;
std::vector<ChildEntity*> m_ActiveChildren;
public:
//Funcs
ParentEntity(unsigned expectedChildren) { m_Children.reserve(expectedChildren); }
ChildEntity* AddChild(){
m_Children.push_back(ChildEntity(*this));
return &(m_Children.back());
}
void RemoveChild(unsigned iterator) {
//Can't figure a way to remove from the m_Children list without disrupting all pointers.
//m_Children.erase(m_Children.begin() + iterator); Uses Copy operators, which wont work as Const values will be present in Child
}
void AddActiveChild(ChildEntity* activeChild) {
m_ActiveChildren.push_back(activeChild);
}
bool Update(){ //Checks if Children are active,
if (!m_ActiveChildren.empty()) {
std::vector<ChildEntity*> TempActive;
TempActive.reserve(m_ActiveChildren.size());
for (unsigned i = 0; i < m_ActiveChildren.size(); i++) {
if (m_ActiveChildren[i]->Update()) {
TempActive.push_back(m_ActiveChildren[i]);
}
}
if (!TempActive.empty()) {
m_ActiveChildren = TempActive;
return true;
}
else {
m_ActiveChildren.clear();
return false;
}
}
else {
return false;
}
}
};
class ChildEntity {
public:
ChildEntity(ParentEntity& Origin) //Not const because it will call Origin functions that alter the parent
:
m_Origin(Origin)
{}
void SetActive() {
m_ChildActive = true;
m_Origin.AddActiveChild(this);
}
bool Update() { //Psuedo job which causes state switch
srand(unsigned(time(NULL)));
if ((rand() % 10 + 1) > 5) {
m_ChildActive = false;
}
return m_ChildActive;
}
private:
ParentEntity& m_Origin;
bool m_ChildActive = false;
};
class ActivatorEntity {
std::vector<ChildEntity*> ActivationTargets;
public:
ActivatorEntity(unsigned expectedTargets) { ActivationTargets.reserve(expectedTargets); }
void AddTarget(ParentEntity& Target) {
ActivationTargets.push_back(Target.AddChild());
}
void RemoveTarget(unsigned iterator) {
ActivationTargets.erase(ActivationTargets.begin() + iterator);
}
void Activate(){
for (unsigned i = 0; i < ActivationTargets.size(); i++) {
ActivationTargets[i]->SetActive();
}
}
};
综上所述,我的三个问题是:
- 有没有办法在向量调整大小时更新指针?
添加 Child 时,如果超出预期容量,则向量会创建一个新数组并将原始对象移动到新位置。这会破坏所有 Activator 指针和任何 m_ActiveChild 指针,因为它们指向旧位置。
- 有没有办法从 m_Children 向量中删除子对象?
由于 ChildEntity 对象将在其中托管 const 项,因此复制分配操作将无法顺利进行,并且 Vector 的擦除功能将无法正常工作。 m_Children 向量可以通过临时向量和复制构造函数在没有不需要的对象的情况下重建,但这会导致所有指针再次出错。
- 如果有任何其他建议的优化或更正,请告诉我!
感谢大家的帮助!
【问题讨论】:
-
没有回答您的具体问题,但您是否考虑过使用不会出现插入、调整大小和删除问题的存储(例如 std:list)?
-
将
std::vector替换为std::list。您将失去缓存友好性、随机访问,并会增加内存占用,但您将消除地址失效的问题。 -
开枪,我从来没想过名单。如果我理解正确,随机访问不是问题,因为指针访问对象,并且当前更大的内存占用是可以的。我担心缓存友好性。并非所有的孩子都在给定的时间被召唤,但大多数可能是。据我了解,Vector 在访问部分时将整个数组加载到缓存中,如果访问多个对象,这在性能方面会更快,对吧....还是错了?不管怎样,谢谢两位!我将开始测试,因为在缓存之外,这感觉是前两个问题的完美解决方案!
-
你的问题和你的代码太长了!你应该用最少的代码问一个问题来重现问题。话虽如此,阅读文档以通过了解失效规则和性能特征来选择最佳容器是一个好的开始。
vector或unique_ptr或shared_ptr在您的情况下可能是更好的选择。如果可能,您应该尽量减少使用的容器数量,以尽量减少数据不一致的风险(例如在某些情况下忘记更新辅助数据)。如果您的数据有可用的密钥,std::map也可能有意义。 -
您可能会研究 std:pmr,(多态内存分配器),它可以提供竞技场分配,以使您的列表分配相对本地化以提高缓存性能。可以加快速度。
标签: c++ pointers vector stdvector