以下代码编译但在运行时发送错误:
# 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
重叠!
我也想知道如何解决这个问题?谢谢!
答案 0 :(得分:3)
您应首先阅读Rule of 3/5/0。你的陈述:
b = a;
违反了规则3(如果您使用的是现代C ++,则为5,即C ++ 11或更高版本),因为您的类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的统治世界。
答案 1 :(得分: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;
}
本声明
b.name = "abc"
也错了。字符串文字具有静态存储持续时间。所以你可能不会删除他们的记忆。
答案 2 :(得分:0)
您将指针从实例a复制到实例b。
当实例a的析构函数运行时,它会删除内存。
当实例b的析构函数运行时,它会再次删除相同的内存。
您需要向此类添加副本和赋值构造函数。 (如果你正在使用c ++ 11,那么移动构造函数)
答案 3 :(得分:0)
我不明白为什么它说&#34; 0x10f9bcf64:被释放的指针是 未分配&#34;因为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
和_id
private
。