#include <iostream>
#include <string>
#include <cstring>
using namespace std;
class MyString
{
char *p;
public:
MyString(const char *s)
{
if (s)
{
p = new char[strlen(s) + 1];
strcpy(p, s);
}
else
p = NULL;
cout << "conversion" << endl;
}
~MyString()
{
if (p)
delete[] p;
cout << "destructor" << endl;
}
};
int main()
{
char str1[10] = {'a'}, str2[10] = {'b'};
MyString s = str1; //***
s = str2; //@@@
cout << "end" << endl; //###
;
}
Outcome:
conversion
conversion
destructor
end
这段代码会在整个程序的最后报错(文件名叫test):
test(12623,0x111f37e00) malloc: *** error for object 0x7fd673405a80: pointer being freed was not allocated
test(12623,0x111f37e00) malloc: *** set a breakpoint in malloc_error_break to debug
zsh: abort
我觉得是这样的:***这行利用类型转换构造函生成了对象s没有问题;但是@@@这行里,编译器会自作聪明地利用类型转换构造函数生成临时对象MySrting temp(str2), 然后进行默认对象赋值,将s的p指针指向临时对象生成时new出来的空间,随即temp的析构函数被调用,然后###这行运行完毕后,s的析构函数被调用,但是由于s的p指向的空间在temp析构函数被调用时已经被delete掉了,因此报错。
如果加上这样一段复制构造函数,仍然有相同问题,因为@@@那行时s对象已经生成了,根本不会调用复制构造函数复制临时对象生成s。
MyString(const MyString &str)
{
if (str.p)
{
p = new char[strlen(str.p) + 1];
strcpy(p, str.p);
}
else
p = NULL;
cout << "operator=(MyString)" << endl;
}
如果有运算符重载,定义对象之间的赋值运算符就不会有问题了:
MyString &operator=(const MyString &str)
{
if (p)
delete[] p;
if (str.p)
{
p = new char[strlen(str.p) + 1];
strcpy(p, str.p);
}
else
p = NULL;
cout << "operator=(MyString)" << endl;
return *this;
}
outcome:
conversion
conversion
operator=(MyString)
destructor
end
destructor
如果定义char*到MyString的赋值运算符重载也可以,那样就不会生成临时对象了。
MyString &operator=(const char *s)
{
if (p)
delete[] p;
if (s)
{
p = new char[strlen(s) + 1];
strcpy(p, s);
}
else
p = NULL;
cout << "operator=(char *)" << endl;
return *this;
}
outcome:
conversion
operator=(char *)
end
destructor