在C++中为两个结构对象使用一个动态数组

2q5ifsrm  于 2024-01-09  发布在  其他
关注(0)|答案(1)|浏览(149)

我试着在几个编译器上运行代码,得到了不同的结果。我要么得到了一些奇怪的退出代码,比如-1073741819 (0xC0000005)free(): double free detected in tcache 2,要么在调试时得到了SIGTRAP (Trace/breakpoint trap) SIGSEGV (Segmentation fault)
我99%确定问题是我释放了两次ships数组。但是为什么当我没有在两个玩家上运行func()两次或者我修改了代码中的其他内容时,我没有得到错误?问题到底是什么?我如何修复错误?

是的我使用动态数组而不是向量,因为我正在尝试练习和学习。是的我知道这种内存分配方法会产生内存碎片,但我从不更改矩阵的大小,所以这应该不是问题。

代码:

  1. struct point_t {
  2. int x;
  3. int y;
  4. point_t();
  5. };
  6. point_t::point_t() {
  7. this->x = 0;
  8. this->y = 0;
  9. }
  10. struct ship_t {
  11. int size;
  12. point_t end_coords[2];
  13. };
  14. struct player_t {
  15. int **map;
  16. int map_size;
  17. ship_t *ships;
  18. int ships_count;
  19. player_t(int, ship_t *, int);
  20. ~player_t();
  21. };
  22. player_t::player_t(int map_size, ship_t *ships, int ships_count) {
  23. this->map = nullptr;
  24. this->map_size = map_size;
  25. this->ships = ships;
  26. this->ships_count = ships_count;
  27. }
  28. player_t::~player_t() {
  29. // Free map memory
  30. // Free each column (sub-array)
  31. for(int i = 0; i < map_size; i++) {
  32. delete[] map[i];
  33. }
  34. // Free each row (array of pointers)
  35. delete[] map;
  36. map = nullptr;
  37. map_size = 0;
  38. delete[] ships;
  39. ships = nullptr;
  40. ships_count = 0;
  41. }
  42. void func(player_t &player) {
  43. player.map = new int *[player.map_size];
  44. for(int i = 0; i < player.map_size; i++) {
  45. player.map[i] = new int[player.map_size];
  46. for(int j = 0; j < player.map_size; j++) {
  47. player.map[i][j] = 0;
  48. }
  49. }
  50. }
  51. int main()
  52. {
  53. ship_t *ships = new ship_t[(5 * 5) / 2];
  54. for(int i = 0; i < 5; i++) {
  55. ships[i].size = i + 1;
  56. }
  57. player_t player1 = player_t(5, ships, 5);
  58. player_t player2 = player_t(5, ships, 5);
  59. func(player1);
  60. func(player2);
  61. return 0;
  62. }

字符串

mtb9vblg

mtb9vblg1#

用Visual Studio运行你的代码给了我:

  1. HEAP CORRUPTION DETECTED: after Normal block (#72) at 0x00B065E8.
  2. CRT detected that the application wrote to memory after end of heap buffer.

字符串
在做delete[] ships;的时候。
第一个问题是你试图在ships构造函数中初始化点,但是:

  1. this->end_coords[2] = {};


这条语句将一个默认的初始化点赋给ships的end_coords成员中的第三个点。2不幸的是,数组有两个点大。
此外,由于player_t析构函数会销毁船只,所以你将船只的所有权交给了player_t。但是你将同一个动态分配数组的所有权交给了两个玩家:

  1. int main()
  2. {
  3. ship_t *ships = new ship_t[(5 * 5) / 2];
  4. for(int i = 0; i < 5; i++) {
  5. ships[i].size = i + 1;
  6. }
  7. player_t player1 = player_t(5, ships, 5); // <-- ships
  8. player_t player2 = player_t(5, ships, 5); // <-- ships


不是一个好主意。同样的船阵被两个玩家使用,这没有意义,因为每个玩家都应该有自己的船,我想。
最后,你不应该调用player的析构函数,因为它会在返回(作用域退出)时再次被调用。

最小修改固定代码:

ship_t构造函数中注解掉这一行:

  1. //this->end_coords[2] = {};


分配两艘船阵,给予每个玩家所有权:

  1. ship_t *ships = new ship_t[(5 * 5) / 2];
  2. for (int i = 0; i < 5; i++) {
  3. ships[i].size = i + 1;
  4. }
  5. player_t player1 = player_t(5, ships, 5);
  6. ship_t *ships2 = new ship_t[(5 * 5) / 2];
  7. for (int i = 0; i < 5; i++) {
  8. ships2[i].size = i + 1;
  9. }
  10. player_t player2 = player_t(5, ships2, 5);

更C++风格的解决方案

让我们用它们的名字来调用结构体。然后在结构体中使用默认的初始化来避免无用的构造函数。删除所有的分配,除非真的真的需要。让我们使用std::vector<>。我不喜欢矢量的矢量用于Map,但在这种情况下,它确实是一个简单的解决方案。使用矢量时不需要存储大小。最后,使用move将ship矢量复制到每个播放器中,以防有什么临时的东西传来传去

  1. #include <vector>
  2. struct point {
  3. int x = 0;
  4. int y = 0;
  5. };
  6. struct ship {
  7. int size = 0;
  8. point end_coords[2];
  9. };
  10. struct player {
  11. std::vector<std::vector<int>> map;
  12. std::vector<ship> ships;
  13. player(int map_size, std::vector<ship> ships_) :
  14. map(map_size, std::vector<int>(map_size)),
  15. ships(std::move(ships_)) {}
  16. };
  17. int main()
  18. {
  19. std::vector<ship> ships;
  20. for (int i = 0; i < 5; ++i) {
  21. ships.push_back({ i + 1 });
  22. }
  23. player player1(5, ships);
  24. player player2(5, ships);
  25. return 0;
  26. }

更C++风格的版本,带内存分配:

这里我没有使用std::vector(但这不是一个好主意)。注意我们如何将一个vector int初始化为0,并且你不需要一个指向指针的指针。通过几个操作符,管理Map会容易得多。作为一个例子,我正在打印你的Map。建议:不要使用ij。行和列使用rc,以避免意外交换它们的含义。

  1. #include <iostream>
  2. struct point {
  3. int x = 0;
  4. int y = 0;
  5. };
  6. struct ship {
  7. int size = 0;
  8. point end_coords[2];
  9. };
  10. // use _ after names, to make clear that these are members
  11. struct player {
  12. int *map_; // owned
  13. int map_size_;
  14. ship *ships_; // owned
  15. int ships_count_;
  16. // player constructor copies the ships vector
  17. player(int map_size, const ship *ships, int ships_count) :
  18. map_size_{ map_size }, ships_count_{ ships_count }
  19. {
  20. map_ = new int[map_size * map_size]{}; // notice the initializer here to default zero
  21. ships_ = new ship[ships_count];
  22. }
  23. // Rule of 4: if you own memory, you need all these 4 methods. Or delete them.
  24. player(const player&) = delete;
  25. player& operator=(const player&) = delete;
  26. ~player() {
  27. delete[] map_; // no setting to nullptr: these will never be accessed anymore
  28. delete[] ships_;
  29. }
  30. int size() const { return map_size_; }
  31. int& operator()(int r, int c) {
  32. return map_[r * map_size_ + c];
  33. }
  34. const int& operator()(int r, int c) const {
  35. return map_[r * map_size_ + c];
  36. }
  37. };
  38. std::ostream& operator<<(std::ostream& os, const player& p) {
  39. auto size = p.size();
  40. for (int r = 0; r < size; ++r) {
  41. for (int c = 0; c < size; ++c) {
  42. if (p(r, c) == 0) {
  43. std::cout << "~";
  44. }
  45. else {
  46. std::cout << p(r, c);
  47. }
  48. }
  49. std::cout << "\n";
  50. }
  51. return os;
  52. }
  53. int main()
  54. {
  55. ship ships[5];
  56. for (int i = 0; i < 5; ++i) {
  57. ships[i].size = i + 1;
  58. }
  59. player player1(5, ships, 5);
  60. player player2(5, ships, 5);
  61. std::cout << "player1:\n" << player1 << "\n";
  62. std::cout << "player2:\n" << player2 << "\n";
  63. return 0;
  64. }

展开查看全部

相关问题