C++中通过this指针调用父类成员的多态问题及性能优化咨询
问题背景
现有类结构:
- 抽象接口
CDbAccessor,包含纯虚方法GetValue() - 派生类
CPagingDbAccessor:实现分页逻辑的Accessor - 派生类
CSpecialDbAccessor:包含大量特殊业务逻辑的Accessor
需要为CSpecialDbAccessor添加分页功能,曾尝试多重继承方案:
class CSpecialPagingDbAccessor : public CSpecialDbAccessor, public CPagingDbAccessor{};
但因菱形继承问题被排除。
当前实现方案中,CSpecialPagingDbAccessor继承CSpecialDbAccessor,内部持有CPagingDbAccessor实例,通过SetCurrentTable()切换使用的Accessor。但切换到etCompany(使用原CSpecialDbAccessor逻辑)时,因多态机制导致GetValue()自调用,触发死循环。同时,若在GetValue()中每次通过switch判断currentTable,会因数据量巨大影响性能。
现有问题代码:
class CDbAccessor { public: virtual std::string GetValue() = 0; }; class CPagingDbAccessor : public CDbAccessor { public: virtual std::string GetValue() override { return "CPagingDbAccessor::GetValue();"; } }; class CSpecialDbAccessor : public CDbAccessor { public: virtual std::string GetValue() override { return "CSpecialDbAccessor::GetValue();"; } }; class CSpecialPagingDbAccessor : public CSpecialDbAccessor { CDbAccessor* pCurrentAccessor; CPagingDbAccessor m_pagingAccessor; int currentTable; public: enum { etPerson, etCompany }; void SetCurrentTable(int entity) { currentTable = entity; switch (entity) { case etPerson: pCurrentAccessor = &m_pagingAccessor; break; case etCompany: pCurrentAccessor = (CSpecialDbAccessor*)this; break;// 此处因多态导致递归 default: break; } } virtual std::string GetValue() override { return pCurrentAccessor->GetValue(); // 多态绑定到自身,死循环 } }; int main(int argc, wchar_t* argv[]) { CSpecialPagingDbAccessor accessor; accessor.SetCurrentTable(CSpecialPagingDbAccessor::etCompany); cout << accessor.GetValue() << endl; accessor.SetCurrentTable(CSpecialPagingDbAccessor::etPerson); cout << accessor.GetValue() << endl; // 死循环 }
问题分析
死循环根源:当设置etCompany时,pCurrentAccessor指向this(CSpecialPagingDbAccessor实例),而GetValue()是虚函数,调用pCurrentAccessor->GetValue()会触发动态绑定,最终调用CSpecialPagingDbAccessor::GetValue(),形成递归调用。
优化解决方案
方案1:使用函数指针/std::function绑定目标实现
在SetCurrentTable()时,直接绑定要调用的函数逻辑,避免通过虚指针间接调用,彻底绕过多态递归。
修改后的CSpecialPagingDbAccessor代码:
#include <functional> class CSpecialPagingDbAccessor : public CSpecialDbAccessor { std::function<std::string()> m_getValueFunc; CPagingDbAccessor m_pagingAccessor; int currentTable; public: enum { etPerson, etCompany }; void SetCurrentTable(int entity) { currentTable = entity; switch (entity) { case etPerson: // 绑定分页Accessor的GetValue m_getValueFunc = [this]() { return m_pagingAccessor.GetValue(); }; break; case etCompany: // 显式绑定基类的GetValue实现,绕过多态 m_getValueFunc = [this]() { return CSpecialDbAccessor::GetValue(); }; break; default: break; } } virtual std::string GetValue() override { return m_getValueFunc(); } };
优势:每次GetValue()仅调用函数指针,性能开销可忽略;逻辑清晰,避免多态陷阱。
方案2:组合而非继承(推荐)
放弃CSpecialPagingDbAccessor继承CSpecialDbAccessor,改为同时持有CSpecialDbAccessor和CPagingDbAccessor实例,自身实现CDbAccessor接口。彻底避免继承带来的多态问题和菱形继承风险。
代码示例:
class CSpecialPagingDbAccessor : public CDbAccessor { CSpecialDbAccessor m_specialAccessor; CPagingDbAccessor m_pagingAccessor; CDbAccessor* pCurrentAccessor; int currentTable; public: enum { etPerson, etCompany }; void SetCurrentTable(int entity) { currentTable = entity; switch (entity) { case etPerson: pCurrentAccessor = &m_pagingAccessor; break; case etCompany: pCurrentAccessor = &m_specialAccessor; break; default: break; } } virtual std::string GetValue() override { return pCurrentAccessor->GetValue(); } // 若需要CSpecialDbAccessor的其他特殊方法,可添加转发函数 void SomeSpecialBusinessLogic() { m_specialAccessor.SomeSpecialBusinessLogic(); } };
优势:符合"组合优于继承"的设计原则,类结构更清晰;完全规避多态递归和菱形继承问题;扩展性更强,后续新增其他Accessor类型时无需修改继承链。
方案3:拆分虚函数与核心实现
在CSpecialDbAccessor中将核心逻辑拆分为非虚方法,虚函数GetValue()调用该核心方法。CSpecialPagingDbAccessor需要使用原逻辑时,直接调用核心方法而非虚函数。
代码示例:
class CSpecialDbAccessor : public CDbAccessor { protected: // 非虚的核心实现,供子类直接调用 std::string DoGetValue() { return "CSpecialDbAccessor::GetValue();"; } public: virtual std::string GetValue() override { return DoGetValue(); } }; class CSpecialPagingDbAccessor : public CSpecialDbAccessor { CDbAccessor* pCurrentAccessor; CPagingDbAccessor m_pagingAccessor; int currentTable; public: enum { etPerson, etCompany }; void SetCurrentTable(int entity) { currentTable = entity; switch (entity) { case etPerson: pCurrentAccessor = &m_pagingAccessor; break; case etCompany: pCurrentAccessor = nullptr; break; // 标记使用基类核心逻辑 default: break; } } virtual std::string GetValue() override { if (pCurrentAccessor) { return pCurrentAccessor->GetValue(); } else { return DoGetValue(); // 直接调用基类核心方法,无多态递归 } } };
优势:仅需少量修改原有类结构;GetValue()中仅需一次nullptr判断,性能开销极小。
内容的提问来源于stack exchange,提问作者Zhang

