C++析构函数问题

发布于 2024-10-15 08:57:19 字数 1224 浏览 6 评论 0原文

我在 C++ 中有一个奇怪的“析构函数”行为

这是我调用的代码:

_log->OnCommit();
delete _log;

问题是当我调用“delete _log;”时它崩溃是因为变量“Entries”无效!!!!

你知道为什么吗?

这是我的班级代码:

struct TransactionLogEntry {
    DependencyObject* Object;
    bool IsAttached;
    bool IsDeleted;
    bool IsUpdated;
};

class TransactionLog
{
public:
    TransactionLog();
    ~TransactionLog();

    void OnCommit();

    map<DependencyObject*, TransactionLogEntry*> Entries;
};

void TransactionLog::OnCommit()
{
    map<DependencyObject*, TransactionLogEntry*>::iterator it;

    for(it = Entries.begin(); it != Entries.end(); it++)
    {
        TransactionLogEntry* entry = (TransactionLogEntry*)(*it).second;
        if (entry->IsDeleted)
            delete entry->Object;

        delete entry;
    }

    Entries.clear();
}

TransactionLog::~TransactionLog()
{
    map<DependencyObject*, TransactionLogEntry*>::iterator it;

    for(it = Entries.begin(); it != Entries.end(); it++)
    {
        TransactionLogEntry* entry = (TransactionLogEntry*)(*it).second;
        delete entry;
    }

    Entries.clear();
}

I have a strange 'destructor' Behavior in C++

Here is the code I call :

_log->OnCommit();
delete _log;

The problem is that when I call "delete _log;" it crash because the variable 'Entries' is invalid !!!!

Do you know why ?

Here is my class code :

struct TransactionLogEntry {
    DependencyObject* Object;
    bool IsAttached;
    bool IsDeleted;
    bool IsUpdated;
};

class TransactionLog
{
public:
    TransactionLog();
    ~TransactionLog();

    void OnCommit();

    map<DependencyObject*, TransactionLogEntry*> Entries;
};

void TransactionLog::OnCommit()
{
    map<DependencyObject*, TransactionLogEntry*>::iterator it;

    for(it = Entries.begin(); it != Entries.end(); it++)
    {
        TransactionLogEntry* entry = (TransactionLogEntry*)(*it).second;
        if (entry->IsDeleted)
            delete entry->Object;

        delete entry;
    }

    Entries.clear();
}

TransactionLog::~TransactionLog()
{
    map<DependencyObject*, TransactionLogEntry*>::iterator it;

    for(it = Entries.begin(); it != Entries.end(); it++)
    {
        TransactionLogEntry* entry = (TransactionLogEntry*)(*it).second;
        delete entry;
    }

    Entries.clear();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

静待花开 2024-10-22 08:57:19

如果没有完整的代码,很难看出,但是我可以注意到你违反了三巨头的规则(你有一个析构函数,但没有复制构造函数或赋值运算符),这意味着寻找麻烦。

我的大胆猜测是,您正在复制构建日志或其他类似问题,一旦您进入 UB 模式,那么任何事情都可能发生,包括在应该没问题的地方引发错误。

It's hard to see without complete code, however I can notice that you're violating the rule of the big three (you have a destructor, but no copy constructor or assignment operator) and this means looking for troubles.

My wild guess is that you're copy-constructing log or other similar problems and once you enter in UB mode then anything can happen, including raising errors in places that should be ok.

耀眼的星火 2024-10-22 08:57:19

您是否在 Entries 映射中存储裸指针?如果是这样,您应该研究使用 boost::shared_ptr (或者 tr1::shared_ptr 如果您有的话)。这极大地简化了存储管理(例如,您可以删除 TransactionLog::OnCommit() 中的 for 循环,而只需调用 Entries.clear() >。

Are you storing naked pointers in the Entries map? If so, you should investigate using boost::shared_ptr (or tr1::shared_ptr if youve got that) instead. This greatly simplifies storage management (for example you can delete the for loop in TransactionLog::OnCommit(), and just call Entries.clear().

忱杏 2024-10-22 08:57:19

如上所述,您缺少 TransactionLog 的复制构造函数和赋值运算符。这是问题的简化版:

struct A {
  int *p;
  A() : p (new int()) {}
  ~A() { delete p; }
}

这正确地分配和销毁了一个对象,对吗?不完全是:

void example() {
  A a;
  A b = a;  // Missing copy ctor; could also happen when passing/returning by value.
  b = a;  // Same result through missing assignment operator.
  assert(a.p == b.p);  // Here is the problem!
  // When b is destroyed, it deletes the pointer.
  // When a is destroyed, it attempts to delete the deallocated pointer,
  // leading to undefined behavior.
}

这里是更接近您的代码的同一问题:

struct A {
  map<string, int*> m;
  A() {
    m["abc"] = new int();
  }
  ~A() {
    for (map<string, int*>::iterator x = m.begin(); x != m.end(); ++x) {
      delete x->second;
    }
  }
  void on_commit() {
    for (map<string, int*>::iterator x = m.begin(); x != m.end(); ++x) {
      delete x->second;
    }
    m.clear();
  }
}

解决方案是为您的类声明一个复制构造函数和赋值运算符。即使您的类是“不可复制的”,您仍然应该声明它们,但将它们设为私有并且不要定义它们:

struct A {
  int *p;
  A() : p (new int()) {}
  ~A() { delete p; }

private:
  A(A const&);
  A& operator=(A const&);
}

当它们是私有的时,任何使用(在不可访问的上下文中)都将是编译器错误。

As said, you're missing a copy ctor and assignment operator for TransactionLog. Here is the problem, simplified:

struct A {
  int *p;
  A() : p (new int()) {}
  ~A() { delete p; }
}

This properly allocates and destroys an object, right? Not quite:

void example() {
  A a;
  A b = a;  // Missing copy ctor; could also happen when passing/returning by value.
  b = a;  // Same result through missing assignment operator.
  assert(a.p == b.p);  // Here is the problem!
  // When b is destroyed, it deletes the pointer.
  // When a is destroyed, it attempts to delete the deallocated pointer,
  // leading to undefined behavior.
}

And here is the same problem written more closely to your code:

struct A {
  map<string, int*> m;
  A() {
    m["abc"] = new int();
  }
  ~A() {
    for (map<string, int*>::iterator x = m.begin(); x != m.end(); ++x) {
      delete x->second;
    }
  }
  void on_commit() {
    for (map<string, int*>::iterator x = m.begin(); x != m.end(); ++x) {
      delete x->second;
    }
    m.clear();
  }
}

The solution is to declare a copy ctor and assignment operator for your class. Even if your class is "non-copyable", you should still declare them, but make them private and don't define them:

struct A {
  int *p;
  A() : p (new int()) {}
  ~A() { delete p; }

private:
  A(A const&);
  A& operator=(A const&);
}

When they are private, any use (in an inaccessible context) will be a compiler error.

分分钟 2024-10-22 08:57:19

看看 OnCommit 函数中的内容:

...
if (entry->IsDeleted)
    delete entry->Object;

delete entry;
...

看起来像是检查某些内容是否被删除。如果是这样,您删除其中的某些内容并始终再次删除该对象。看起来你是在问问题。

Looking at what you have in the OnCommit function:

...
if (entry->IsDeleted)
    delete entry->Object;

delete entry;
...

it looks like you check if something is deleted. If so, you delete something inside it and always delete the object again. Looks like you're asking for problems.

ら栖息 2024-10-22 08:57:19

这里可能有2美分:

  1. 正如之前所说,尽量避免使用裸c/c++指针指向对象,而是使用某种智能ptr(例如boost::shared_ptr)或auto_ptr来简单地持有/释放资源(但是由于 auto_ptr 特定,所以不在 stl 容器中)

  2. 关于此崩溃:没有什么可以阻止您使用不同的键但相同的值填充地图对象。因此有可能删除对象两次或更多次,这肯定会导致崩溃(如果指针等于 0,则可以删除指针两次或更多次)。所以这样写:

    删除指针;
    ptr = 0;

而不是仅仅删除一个指针

here may 2 cents:

  1. as it was said before, try to avoid using naked c/c++ pointers to objects, instead use some kind of smart ptr (e.g. boost::shared_ptr) or auto_ptr for simple holding/releasing resources (but not in stl containers due to auto_ptr specific)

  2. about this crash: there is nothing to prevent you from filling map object with different keys but the same values. so it becomes possible to delete object twice or more times which definitely leads to crash (you can delete pointer twice or more if it is equal to 0). so write this:

    delete ptr;
    ptr = 0;

instead of just deleting a pointer

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文