了解过C++语言的人,都应该知道,C++语言中的构造函数,析构函数,拷贝构造函数,赋值运算符重载函数,如果不定义,编译器会自动生成的,当然,生成的只是一些最基本的,在达不到我们要求的条件下,就需要我们自己重新定义这些函数。
我们现在说的讲的是深拷贝与浅拷贝,当然讨论这个问题的基础,一般情况下是我们定义的变量是以指针形式出现的,原因就在于,不论赋值还是拷贝,我们要实现两个指针指向的内容一致,那我得到新的指针和原指针是指向同一块内存空间还是两块内存空间的相同内容。如果是指向同一块空间,就存在了安全隐患,我们在自己管理空间时,就很有可能对一块空间进行了多次的释放,有些编译器下会报错,甚至直接奔溃,有些编译器虽不会抛出异常,但也会对后续的程序造成未知的错误。
那么,现在就以String为例,看看如何解决这些问题。
//参数列表中,pstr为char* 类型,s为String类型
//实现方法1:
class String
{
friend ostream& operator<<(ostream& _out, const String& s);
public:
String(const char* pstr) :_str(new char[strlen(pstr)+1])
{
strcpy(_str,pstr);
cout << "构造" << endl;//做标识
}
String(const String& s) :_str(new char[strlen(s._str) + 1])
{
strcpy(_str,s._str);
cout << "拷贝构造"<<endl;//做标识
}
String& operator = (const String& s)
{
if (this == &s)//不给自己赋值,防止对一块空间进行二次析构
{
char* tmp = (new char[strlen(s._str) + 1]);
strcpy(tmp, s._str);
delete[]_str;
_str = tmp;
}
cout << "赋值重载" << endl;//做标识
return *this; //注意返回值
}
~String()
{
if (this != NULL)
{
delete[]_str;
_str = NULL;
}
cout << "析构" <<endl;//做标识
}
private:
char *_str;
};
//输出运算符重载
ostream& operator<<(ostream& _out,const String& s)
{
_out << s._str<< endl;
return _out;
}
//*****************************************************************************************
//String类的实现2
class String
{
friend ostream& operator<<(ostream& _out, const String& s);
public:
String(const char* str) :_str(new char[strlen(str)+1])//同上
{
strcpy(_str,str);
}
//拷贝构造
String(const String& s) : _str(NULL)
{
String tmp(s._str);
std::swap(_str,tmp._str);
}
//赋值运算符重载
//String&operator=(const String& s)
//{
// if (this != &s)
// {
// String tmp(s._str);//用s._str或者s实例化tmp都可以
// std::swap(_str,tmp._str);
// }
// return *this;
//}
//赋值运算符重载之开辟空间法
String&operator=(const String& s)
{
if (this != &s)
{
char* tmp = new char[strlen(s._str) + 1];
strcpy(tmp,s._str);
delete[]_str;
_str = tmp;
}
return *this;
}
//析构
~String()
{
if (NULL == _str)
{
delete[] _str;
_str = NULL;
}
}
private:
char* _str;
};
//输出运算符重载
ostream& operator<<(ostream& _out,const String& s)
{
_out << s._str<< endl;
return _out;
}
//*****************************************************************************************
// String类的实现3------>浅拷贝实现防止内存的多次释放
class String
{
friend ostream& operator<<(ostream& _out, const String& s);
public:
String(const char* pdata)
:_str(new char[strlen(pdata)+1])
, _count(new int)
{
strcpy(_str,pdata);
*_count = 1;
}
String(const String& s)
:_str(s._str)
, _count(s._count)
{
_count++;
}
String& operator=(const String& s)
{
if (this != &s)
{
if (--(*_count) == 0)
{
delete[]_str;
delete _count;
}
_count = s._count;
_str = s._str;
_count++;
}
return* this;
}
~String()
{
if (--(*_count) == 0)
{
delete[] _str;
delete _count;
}
}
private:
char* _str;
int* _count;
};
//输出运算符重载
ostream& operator<<(ostream& _out,const String& s)
{
_out << s._str<< endl;
return _out;
}
//*****************************************************************************************
友元函数受第一参数的限制,只能采用类外定义,引入友元
方法三,关于引入计数器,在面试过程中,这是最不推荐的方法,HR往往更倾向于让你写出深拷贝的方法。
深拷贝与浅拷贝是面试过程中经常出现的题目,弄清楚这类问题是非常必要的