【问题标题】:The destructor is unable to delete a continuous memory block that has been allocated析构函数无法删除已分配的连续内存块
【发布时间】:2017-05-08 06:29:26
【问题描述】:

以下代码可以编译,但在运行时会报错:

# include <iostream>
# include <string.h>

class A {
public:
  A() {}
  A ( int id, char * t_name ) {
    _id = id ;
    name = new char [ strlen (t_name) + 1 ] ;
    strcpy ( name, t_name ) ;
  }

  char *name ;
  int _id ;
  ~A() { delete []  name ;}
} ;

int main () {
  A a ( 1, "123" ) ;
  A b ;
  b = a ;

  std::cout << static_cast < const void * > ( a.name ) << std::endl ;
  std::cout << static_cast < const void * > ( b.name ) << std::endl ;

  b.name = "abc" ; // b.name is directed to a different memory block 
  std::cout << static_cast < const void * > ( a.name ) << std::endl ;
  std::cout << static_cast < const void * > ( b.name ) << std::endl ;
  std::cout << a.name << std::endl ;
  std::cout << b.name << std::endl ;

  return 0 ;
}

它输出如下内容:

0x7ff87bc03200
0x7ff87bc03200
0x7ff87bc03200
0x10f9bcf64
123
abc
a.out(883,0x7fff7ee3d000) malloc: *** error for object 0x10f9bcf64:
pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6

我不明白为什么会这样说:

0x10f9bcf64: 被释放的指针未被分配

因为b.name显然是指向0x10f9bcf64,并且不再与a的重叠!

我也想知道如何解决这个问题?谢谢!

【问题讨论】:

  • 你违反了三法则。
  • b.name 不是new 的内存,所以它不可能是deleted

标签: c++ memory destructor


【解决方案1】:

您应该首先阅读Rule of 3/5/0。您的声明:

b = a;

违反了规则 3(如果您使用现代 C++,即 C++11 或更高版本,则为 5),因为您的类 A 有一个指针作为成员。

接下来,如果你考虑这个说法:

b.name = "abc";

您在这里影响了一个您没有使用new 分配的静态字符数组。所以当你的析构函数试图删除它时:

~A() { delete []  name ;}

delete[] 的调用会产生您的错误。

一个简单的解决方案是将name 声明为std::string

class A {
public:
  A () {}
  A ( int id, const std::string& t_name ) {
    _id = id ;
    name = t_name;
  }

  std::string name ;
  int _id ;
} ;

int main () {
  A a ( 1, "123" ) ;
  A b ;
  b = a ;

  std::cout << static_cast < const void * > ( &a.name ) << std::endl ;
  std::cout << static_cast < const void * > ( &b.name ) << std::endl ;

  b.name = "abc" ; // b.name is directed to a different memory block 
  std::cout << static_cast < const void * > ( &a.name ) << std::endl ;
  std::cout << static_cast < const void * > ( &b.name ) << std::endl ;
  std::cout << a.name << std::endl ;
  std::cout << b.name << std::endl ;

  return 0 ;
}

由于std::string为你管理内存,你又回到了0规则的美妙世界。

【讨论】:

    【解决方案2】:

    对于初学者来说,构造函数声明应该是这样的

    A ( int id, const char * t_name )
                ^^^^^^
    

    因为您使用字符串字面量来初始化类的对象,而字符串字面量具有常量数组的类型。

    默认的复制赋值运算符会按成员复制对象的数据成员。相对于您在此语句之后的代码

    b = a ;
    

    对象将有两个指针指向同一个动态分配的内存。因此,对于同一个内存地址,删除操作符将被调用两次。

    您必须为您的类显式编写复制赋值运算符和复制构造函数。

    例如,复制赋值运算符可以如下所示

    A & operator = ( const A &a )
    {
        if ( this != &a )
        {
            char *tmp = new char[ std::strlen( a.name ) + 1 ];
            std::strcpy( tmp, a.name );
    
            delete [] this->name;
    
            this->_id = a._id;
            this->name = tmp;
        }
    
        return *this;
    } 
    

    此声明

    b.name = "abc"
    

    也是错误的。字符串文字具有静态存储持续时间。所以你不能删除他们的记忆。

    【讨论】:

    • 不仅要实现赋值运算符,还要实现复制构造函数和移动操作(参见 3/5/0 的规则),除非因为使用 std::string 而放弃了手动管理内存的想法。此外,错误不是由b = a; 引起的,而是由b.name = "abc"; 引起的(但没有b.name = "abc";b = a; 确实会导致错误)
    • @wasthishelpful 首先你完全错了。错误的原因是缺少复制赋值运算符。其次,我在回答中指出,必须明确定义复制构造函数。第三,不需要为这样简单的程序定义移动特殊功能。
    • 抱歉,您确实提到了复制构造函数^^我们不知道OP背后的真正用法,所以恕我直言,我们不能说不需要定义移动特殊函数。我们在这里提到了它们,我很高兴^^ 我不同意你的观点:OP 代码中删除的内存不是a 复制的指针。如果没有重新分配b.name,那么我会同意你关于OP中错误的原因。除了 OP,缺少的复制赋值运算符确实是一个错误,所以无论如何我同意你的观点:)
    【解决方案3】:

    您正在将指针从实例 a 复制到实例 b。

    当实例a的析构函数运行时,它会删除内存。

    当实例b的析构函数运行时,它再次删除相同的内存。

    您需要向此类添加一个复制和赋值构造函数。 (如果你使用的是 c++11,还有一个移动构造函数)

    【讨论】:

      【解决方案4】:

      我不明白为什么它说“0x10f9bcf64:指针被释放是 未分配”,因为 b.name 显然指向 0x10f9bcf64 并且 不再与 a 重叠!

      因为b 的析构函数在静态字符串上调用delete []

      我也想知道如何解决这个问题。

      你应该已经定义了一个拷贝构造函数,比如:

      A::A(const A& lhs) {
          _id = lhs.id;
          name = new char [ strlen (lhs.name) + 1 ] ;
          strcpy ( name, lhs.name ) ;
      }
      

      还制作了name_idprivate

      【讨论】:

        猜你喜欢
        • 2017-07-06
        • 2016-02-16
        • 2021-11-15
        • 2021-07-16
        • 2011-01-15
        • 2010-10-12
        • 2013-10-04
        • 1970-01-01
        相关资源
        最近更新 更多