【问题标题】:Dynamic allocation using memory locations already in use使用已在使用的内存位置进行动态分配
【发布时间】:2013-12-04 02:10:43
【问题描述】:

我有一个提取运算符用于具有 char* 成员“名称”的类。这是我的主要驱动程序的代码:

  Player tempPlayer;
  for(int i=0;i<4;i++){
     fin >> tempPlayer;
     }

然后我继续对提取的玩家做一些事情(这无关紧要),但问题是每次使用提取运算符时,都会发生一些奇怪的事情。下面是操作符的定义:

ifstream& operator>>(ifstream& fin, Player& currentPlayer){
   char* temp = new char[50];
   char tempChar;
   fin >> temp;
   // importing from a file that contains names of about 6 characters each
   stringCopy(currentPlayer.name, temp);
   delete[] temp;
   temp = NULL;
   return fin;
   }

字符串复制正文:

 void stringCopy(char *destPtr, const char *sourcePtr){
     while(*sourcePtr!='\0'){
        *destPtr = *sourcePtr;
        destPtr++;
        sourcePtr++;
        }
     *destPtr='\0';
     }

我一直在通过打印出用于temp 的内存地址和名称进行调试。

第一次调用提取操作符时,玩家的名字和temp数组有不同的内存地址,这是应该发生的。然后 temp 被删除并设置为 NULL,我通过打印地址(并得到 '0')确认了这一点,并且在函数返回后玩家姓名的地址仍然存在。

但是,在随后的调用中,temp 和玩家姓名的地址都与第一个玩家姓名的地址相同。名称地址应该相同,因为它是刚刚被覆盖的同一个对象,但为什么temp 使用new char[] 关键字分配的地址与“名称”相同?

这是我在调试时使用的一些代码:

沿着操作符主体的每一步:

cout &lt;&lt; "temp address followed by name address: " &lt;&lt; (void*)temp &lt;&lt; " " &lt;&lt; (void*)(currentPlayer.name) &lt;&lt; endl;

在主驱动中:

cout &lt;&lt; "player " &lt;&lt; i+1 &lt;&lt; " has been extracted with name address " &lt;&lt; (void*)(tempPlayer.name) &lt;&lt; endl;

这是Player 构造函数:

Player::Player(){
   name = new char[50];
   stringCopy(name,"name");
   ID = new int[5];
   }

排除不相关的数据成员,这里是Player定义:

class Player{
   public:
      char* name;
};

【问题讨论】:

  • 向我们展示您的 stringCopy 函数的代码。如果您仍然使用 C++,您可能应该使用 std::string。
  • 您为什么说stringCopy 进行“深度”复制? name 也是一个对象吗?请发帖stringCopyPlayer
  • 那么,真的有问题吗?如果动态内存分配给现在空闲但曾经分配过的内存位置,则无需担心。
  • 'name' 的声明只是char* name
  • 向我们展示Player 的构造函数。例如,看到name 没有初始化并且正在从堆栈中拾取垃圾,我不会感到惊讶。

标签: c++ memory dynamic


【解决方案1】:

当您delete[] 某些您已经new[]ed 的东西时,允许标准库重用该内存。因此,temp 一次又一次地获得相同的地址也就不足为奇了。这是预期的行为,本身并不是问题。

真正的问题是,在您的“驱动程序”代码(一些您没有向我们展示的代码)中,您在某处有一个 tempPlayer 的浅拷贝。这会将指针复制到name,而不分配新的存储空间。当该副本被删除时,Player 的析构函数会删除该浅副本中的name,将name 释放到堆中。

现在原始文件指向已释放的内存。将来对您的operator&gt;&gt; 的调用会分配这个现在应该是空闲的内存。糟糕!

您的短期修复是删除浅拷贝。长期的、正确的解决方法是根据“3 规则”(http://en.wikipedia.org/wiki/Rule_of_three_%28C++_programming%29)实现正确的复制和复制分配构造函数,或者如果您想对 C++11 真正友好,则使用 5 规则. (相同的链接。)

或者,您可以考虑将复制和复制分配构造函数设为私有,这样可以防止这些对象的不需要的副本。 (这里有一些提示:What's the most reliable way to prohibit a copy constructor in C++?

【讨论】:

  • 我最初在问题中没有说得很清楚,但问题是在分配内存给temp的行的第二次调用中,新内存的地址是相同的作为已经在使用的name 数组。
  • 对,temp 获得相同的内存是没有问题的,因为您已将其释放以供delete[] 重用。或者你是说&amp;currentPlayer.name == temp?那不应该发生。向我们展示您的调试代码打印的输出。
  • @user1362548: 向我们展示播放器类以及.name 的内存是如何分配/释放的......
  • @Joe Z,这就是我的意思。第一次调用的输出:temp address followed by name address: 0x8ed500 0x8ea2e0 在将 temp 设置为 NULL 后:temp address and name address: 0 0x8ea2e0 然后在第二次调用:temp address and name address: 0x8ea2e0 0x8ea2e0 然后在第二次设置为 null 后:temp address and name address: 0 0x8ea2e0
  • 顺便说一句,cmets在你的答案代码中的位置,我在这里做一些事情,我只知道这与所问的问题无关。
猜你喜欢
  • 2020-11-12
  • 1970-01-01
  • 1970-01-01
  • 2016-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多