对象池与基类赋值操作是否属于C++未定义行为?
老式MMORPG源码中对象池与派生类赋值的疑问
我正在研究一款老式MMORPG的源码,以了解过去的游戏开发实现方式,遇到了一段逻辑如下的简化模拟代码(省略了池类部分):
class VMAIN { public: int nVar1; int nVar2; VMAIN():nVar1(0),nVar2(0){;} virtual ~VMAIN (){;} void Reset() { nVar1=0; nVar2=0; } void Assign(const VMAIN& vSub) { *this = vSub; } VMAIN* GetMAIN(){return this;} }; class VSUB: public VMAIN { public: float fVar1; float fVar2; VSUB():fVar1(0),fVar2(0){;} void Reset() { fVar1 = 0; fVar2 = 0; }; }; std::queue<VMAIN*> vecMain; std::queue<VSUB*> vecSub; VMAIN* NEW_VMAIN() { VMAIN* pMain; if ( vecMain.empty() ) { pMain = new VMAIN; } else { pMain = vecMain.front(); vecMain.pop(); } return pMain; } VSUB* NEW_SUB() { VSUB* pMain = NULL; if ( vecSub.empty() ) { pMain = new VSUB; } else { pMain = vecSub.front(); vecMain.pop(); } return pMain; } void RELEASE_MAIN(VMAIN* pMain) { pMain->Reset(); vecMain.push(pMain); } void RELEASE_SUB(VSUB* vSub) { vSub->Reset(); vecSub.push(vSub); } int main() { std::queue<VSUB*>vSub; for ( int i = 0; i != 5 ; i++) { VMAIN* pMain = NEW_VMAIN(); pMain->nVar1 = i+1; pMain->nVar2 = i+1; VSUB* pSub = NEW_SUB(); pSub->Assign(*pMain); pSub->fVar1 = float(i)+0.5f; pSub->fVar2 = float(i)+0.5f; printf("Iteration: %d , nVar1 %d , nVar1 %d, fVar1 %.2f, fVar2 %.2f\n",i,pSub->nVar1,pSub->nVar2,pSub->fVar1,pSub->fVar2); vSub.push(pSub); } printf("\n Releasing \n \n"); const int nSize = vSub.size(); for ( int i = 0; i < nSize; i++) { VSUB* pSub = vSub.front(); vSub.pop(); printf("Iteration[Before]: %d , nVar1 %d , nVar1 %d, fVar1 %.2f, fVar2 %.2f\n",i,pSub->nVar1,pSub->nVar2,pSub->fVar1,pSub->fVar2); int j = i+10; VMAIN* pMain= NEW_VMAIN(); pMain->nVar1 = j; pMain->nVar2 = j; if ( j < 12 ) { RELEASE_MAIN(pSub->GetMAIN()); pSub->Assign(*pMain); printf("Iteration[After Replacing BASE]: %d , nVar1 %d , nVar1 %d, fVar1 %.2f, fVar2 %.2f\n",i,pSub->nVar1,pSub->nVar2,pSub->fVar1,pSub->fVar2); } else { RELEASE_MAIN(pMain); printf("Iteration[After Not Toching Base Values]: %d , nVar1 %d , nVar1 %d, fVar1 %.2f, fVar2 %.2f\n",i,pSub->nVar1,pSub->nVar2,pSub->fVar1,pSub->fVar2); } RELEASE_SUB(pSub); RELEASE_MAIN(pSub->GetMAIN()); printf("Iteration[After Release]: %d , nVar1 %d , nVar1 %d, fVar1 %.2f, fVar2 %.2f\n\n",i,pSub->nVar1,pSub->nVar2,pSub->fVar1,pSub->fVar2); } int nSizeMain = vecMain.size(),nSizeSub = vecSub.size(); printf("Size of vecMain %d, Size of vecSub %d\n",nSizeMain,nSizeSub); }
我理解这段代码的逻辑是:先计算基类VMAIN的相关值,若符合要求则将其赋值给派生类VSUB,否则将基类对象归还对象池。测试后代码可以正常运行,但我想确认这种实现是依赖C++未定义行为,还是过去游戏开发中常规的实践方式?
核心结论
这段代码的核心逻辑(基类赋值给派生类的基类成员、对象池复用)符合C++标准,不属于未定义行为,但代码中存在明显的逻辑bug,这些bug不属于老式游戏开发的常规正确实践。
细节分析
1. 代码中的明确bug(非未定义行为,属于逻辑错误)
NEW_SUB()函数中,从vecSub队列取对象时,错误调用了vecMain.pop(),这会导致vecMain队列被错误修改,后续对象池的复用逻辑完全混乱。main函数末尾同时调用RELEASE_SUB(pSub)和RELEASE_MAIN(pSub->GetMAIN()),相当于把同一个VSUB对象同时放入vecSub和vecMain两个对象池,后续取出使用时会出现类型不匹配、重复释放等严重问题。
2. 符合C++标准的部分(无未定义行为)
- 基类
VMAIN的Assign函数中使用*this = vSub;:当this指向派生类VSUB对象时,基类赋值运算符只会复制基类部分的成员,派生类成员不受影响,这是C++的标准行为(赋值运算符不支持多态,只会调用当前类型的赋值逻辑)。由于代码中派生类未重写赋值运算符,因此这里的操作完全合法。 GetMAIN()返回this:对于VSUB对象来说,这是合法的向上转型(派生类指针转基类指针),属于C++标准允许的操作。
3. 老式MMORPG的常规实践
- 对象池复用:这是老式MMORPG中非常普遍的性能优化手段,因为服务器需要处理大量频繁创建销毁的对象(如玩家、怪物、技能实例),用对象池避免内存碎片和频繁分配开销是常规操作。
- 基类/派生类分池管理:为不同类型的对象分别维护对象池,避免类型混淆,是合理的设计思路,但你的代码中出现了把派生类对象放入基类池的错误操作,这不是正确的实践。
- 封装赋值操作:用
Assign函数封装赋值逻辑,而非直接使用赋值运算符,在老代码中很常见,目的是统一控制赋值行为,方便后续扩展(如添加校验、日志)。
内容的提问来源于stack exchange,提问作者Azriel Elijay
相关产品推荐
相关产品推荐

