2016-12-22 27 views
4

下面的代码编译,但在运行时提供了一个错误的连续内存块:析构函数是无法删除已分配

# 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重叠!

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

+4

规则的精彩世界你已经违反了法则三。 –

+1

'b.name'不是'new''d内存,所以它不能'delete'd –

回答

1

对于初学者的构造函数声明应该像

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

,因为你使用字符串文字来初始化类和字符串文字的对象有类型的常量数组。

默认的复制赋值操作符使对象的数据成员的成员智能副本。相对于此语句后的代码,

b = a ; 

对象将具有两个指向相同动态分配内存的指针。因此delete操作符将被调用两次以获得相同的内存地址。

你必须明确地写出你的类的复制赋值操作符和复制构造函数。

例如,拷贝赋值运算符可以看看下面的方式

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; 
} 

本声明

​​

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

+0

不仅需要赋值运算符,还要复制构造函数和移动操作(参考3/5/0的规则),除非使用'std :: string'来删除手动管理内存的想法。此外,错误不是由'b = a;'引起的,而是由'b.name =“abc”;'(但没有'b.name =“abc”;','b = a;'确实会导致错误) – wasthishelpful

+0

@wasthishelpful首先你完全错了。错误的原因是没有复制赋值操作符。其次,我在答复中指出,复制构造函数必须明确定义。第三,不需要为这样简单的程序定义移动特殊功能。 –

+0

我的歉意,你确实提到了拷贝构造函数^^我们不知道OP背后的真实用法,所以恕我直言,我们不能说没有必要定义移动特殊函数。我们在这里提到他们,我很满意^^我不同意你的看法:OP代码中的已删除内存是**而不是**从'a'复制的指针。如果没有重新分配“b.name”,那么我会同意你关于OP中错误的原因。除OP之外,缺失的复制赋值操作符确实是一个错误,所以我同意你的意见:) – wasthishelpful

0

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

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

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

您需要为此类添加副本和赋值构造函数。 (和移动的构造函数,如果你正在使用C++ 11)

0

我不明白为什么它说:“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

3

您应该先阅读约Rule of 3/5/0。您的发言:

b = a; 

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

接下来,如果你认为这样的说法:

b.name = "abc"; 

你在这里影响静态字符数组没有用new分配。所以,当你的析构函数试图将其删除:

~A() { delete [] name ;} 

delete[]调用生成您的错误。

一个简单的解决办法是宣布namestd::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