You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

通过成员Erase函数删除自身对象:实现是否合规?是否存在风险?

成员对象自删除的实现安全性问题

我有一个包含成员的容器对象(例如vector),需要通过成员自身的Erase函数将其从容器列表中删除。以下是示例代码:

using namespace std;

class CCont;
class CMember
{
public:
  int m_iIndex;
  CString m_csName;
  CCont *m_pParent;

  CMember(CCont *pParent, CString csName, int iIndex);
  bool Erase(); //return true, if erasing is successful

};

class CCont : protected vector <CMember *>
{
public:
  //Add member to list
  void AddMember(CString csName, int iIndex)
  {
      CMember *pMem = new CMember(this, csName, iIndex);
      push_back(pMem);
  }

  //find member in list by index / name a return pointer
  CMember *FindMember(int iIndex) const
  {
      for(auto memb : *this)
          if(memb->m_iIndex == iIndex)
              return memb;
      return NULL;
  }

  CMember *FindMember(LPCTSTR lsName) const
  {
      for(auto memb : *this)
          if(memb->m_csName == lsName)
              return memb;
      return NULL;
  }

  bool EraseMemb(CMember *pMemb)
  {
      iterator it;
      for(it = begin(); it != end(); ++it)
          if(*it == pMemb)
              break;
      if(it == end())
          return false;
      delete *it;
      erase(it);
      return true;
  }
};

int main()
{
    CCont cCont;
    cCont.AddMember(L"Smith", 1);
    cCont.AddMember(L"Novak", 3);
    cCont.AddMember(L"Johny", 4);
    CMember *pSmith = cCont.FindMember(L"Smith");
    if(pSmith)
        pSmith->Erase();
    //pSmith is now pointer to non-existent object, but I will not use pSmith nevermore

    int debug = 0;
    return 0;
}

//--------implementation----------

CMember::CMember(CCont *pParent, CString csName, int iIndex)
    : m_pParent(pParent), m_csName(csName), m_iIndex(iIndex)
{}

bool CMember::Erase()
{
    return m_pParent->EraseMemb(this);
    //here I am returning to function of the non-existing object
}

在该实现中,程序会在某一时刻回到已不存在的对象的函数上下文。请问这种实现方式是否正确?是否会导致程序出现意外行为?


回答

这种实现不正确,会触发C++标准中的未定义行为,存在严重的安全风险。

问题根源

当调用pSmith->Erase()时,流程是:

  1. CMember::Erase()调用m_pParent->EraseMemb(this)
  2. CCont::EraseMemb()中执行delete *it,直接销毁了当前CMember对象,其占用的内存被释放
  3. EraseMemb()返回true,回到Erase()函数的return语句
  4. 此时Erase()函数属于已销毁对象的成员函数,this指针指向的是已释放的内存——虽然这里return只是返回一个布尔值,但标准明确规定:访问已销毁对象的任何部分(包括通过this指针隐式访问)都属于未定义行为。

可能的意外行为

  • 内存检测工具(如Valgrind)会报告"使用已释放内存"的错误
  • 程序可能直接崩溃,或者在后续执行中出现难以追踪的诡异bug(比如内存被复用后的数据污染)
  • 不同编译器的优化策略可能导致不同的异常表现,问题的复现性差

安全的改进方案

可以调整销毁和移除的顺序,确保成员函数返回前对象仍然有效,或者将对象销毁放在函数执行的最后一步:

方案1:先移除容器指针,再销毁对象

修改EraseMemb和Erase的逻辑,让容器先移除指针,再由成员函数自行销毁:

// 修改CCont的EraseMemb函数
bool CCont::EraseMemb(CMember *pMemb)
{
    iterator it = find(begin(), end(), pMemb);
    if(it == end())
        return false;
    erase(it); // 先从容器移除指针,对象仍存在
    return true;
}

// 修改CMember的Erase函数
bool CMember::Erase()
{
    bool success = m_pParent->EraseMemb(this);
    if(success)
        delete this; // 最后一步销毁自己,此时函数即将返回,不会再访问对象成员
    return success;
}

这种方式下,delete this是C++标准允许的操作,只要执行后不再访问对象的任何成员即可。

方案2:使用智能指针管理内存

改用std::unique_ptr<CMember>存储在容器中,让智能指针自动管理内存,避免手动delete的风险:

class CCont : protected vector<std::unique_ptr<CMember>>
{
public:
    void AddMember(CString csName, int iIndex)
    {
        emplace_back(std::make_unique<CMember>(this, csName, iIndex));
    }

    bool EraseMemb(CMember *pMemb)
    {
        auto it = find_if(begin(), end(), [pMemb](const auto& ptr) {
            return ptr.get() == pMemb;
        });
        if(it == end())
            return false;
        erase(it); // 移除时智能指针自动销毁对象
        return true;
    }
    // ... 其他函数调整为适配unique_ptr
};

这种方式无需手动管理内存,从根源上避免了野指针和已销毁对象访问的问题。


内容的提问来源于stack exchange,提问作者Michal Hadraba

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 16:26:15