std::queue でメモリ解放エラーの問題が発生しました

プロジェクトには、メッセージ イベントを順番に処理するために std::queue を使用するという要件があります。

簡単な例は次のとおりです。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

struct MyEvent {

  MyEvent() { event_ = CreateEvent(nullptr, 0, 0, 0); }

  ~MyEvent() { std::cout << "MyEvent deconstruct" << std::endl; }

  void Run() {

    if (event_ != nullptr) {

      SetEvent(event_);

    }

  }

 private:

  HANDLE event_;

};

int main() {

  std::queue<MyEvent> my_event_queue;

  HANDLE event = CreateEvent(nullptr, 0, 0, 0);

  for (int i = 0; i < 3; i++) {

    auto task = new MyEvent();

    my_event_queue.push(*task);

  }

  while (!my_event_queue.empty()) {

    auto my_event = &my_event_queue.front();

    my_event_queue.pop();

    delete my_event;

  }

  return 0;

}

  

テスト ケースでは、合計 3 つのオブジェクトをキュー my_event_queue にプッシュし、while ループとフロント ループを使用してキュー内のオブジェクトのアドレスを取得してポップしました。

問題は、my_event の削除にあります。理論的には、std::queue はオブジェクトの破棄には関与しません。つまり、新しいオブジェクトはそれ自体で削除する必要があるため、オブジェクトをポップアウトするたびに削除します。

その後、while ループが 2 回目に到達したときにアボートが発生したため、メモリを見てみると、2 回目の削除時のメモリが未割り当てであったため、アボートが発生しました。

スクリーンショットからわかるように、ハンドルのサイズは 4 バイトであるため、赤枠 3 つの領域にメモリ領域が割り当てられており、削除のたびに 4 バイトのメモリ領域が消去されるはずです。つまり、1回目は1番目の赤枠を消し、2回目は2番目の赤枠を消します。

しかし実際には、最初の削除でメモリ長の 20 バイトが消去され、その結果、2 回目の削除で未割り当てメモリにアクセスすることになりました。

その後の調査により、プッシュ時にポインタの代わりに値が渡されたため、std::queue がコピー コンストラクタを呼び出すことが原因であることがわかりました (コピー コンストラクタが明示的に定義されていない場合は、デフォルトのコンストラクタが呼び出されます)。実際には保存されたコピーです。

ポップするたびに、コピーはアクティブに破棄され、オントロジーには影響しません (手動で削除する必要があります)。そのため、コピーのポインタを取得し、ポップ後に削除するだけです。このときのアドレスは次のとおりです。すでにダングリング ポインタです。動作は未定義です

20 バイトがキューのデフォルトのサイズであることに注意してください。

どうやって解決すればいいでしょうか?

事前に配列を宣言しておき、その中に new の後のアドレスを入れておき、最後に使用した後に順番に削除することができます。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

MyEvent* task[3];

for (int i = 0; i < 3; i++) {

  task[i] = new MyEvent();

  my_event_queue.push(*task[i]);

  auto task = new MyEvent();

  my_event_queue.push(*task);

}

...

// 此处只是方便测试

delete task[0];

delete task[1];

delete task[2];

 

当然更好的办法是使用智能指针来保证自动释放内存 std::queue<std::unique_ptr<MyEvent>> my_event_queue;

示例:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

#include <Windows.h>

#include <synchapi.h>

#include <iostream>

#include <memory>

#include <queue>

struct MyEvent {

  MyEvent() { event_ = CreateEvent(nullptr, 0, 0, 0); }

  // 添加移动构造函数

  MyEvent(MyEvent&& other) : event_(other.event_) { other.event_ = nullptr; }

  ~MyEvent() {

    if (event_ != nullptr) {

      CloseHandle(event_);  // 显式关闭句柄

    }

    std::cout << "MyEvent deconstruct" << std::endl;

  }

  void Run() {

    if (event_ != nullptr) {

      SetEvent(event_);

    }

  }

 private:

  HANDLE event_;

};

int main() {

  std::queue<std::unique_ptr<MyEvent>> my_event_queue;

  for (int i = 0; i < 3; i++) {

    auto task = std::make_unique<MyEvent>();

    my_event_queue.push(std::move(task));  // 使用 std::move 将对象放入队列

  }

  while (!my_event_queue.empty()) {

    auto& my_event = my_event_queue.front();

    my_event->Run();

    my_event_queue.pop();

  }

  return 0;

}

おすすめ

転載: blog.csdn.net/2301_78834737/article/details/132004587