浅拷贝问题
我们知道,定义一个类的时候,如果我们没有写构造,拷贝构造,赋值运算符重载,析构方法的话,编译器会自动生成。当该类成员中涉及到资源的管理时,实现的就是浅拷贝。所以,以上这几种方式是必须要程序猿手动实现的。
举例来看:
图中所示:实现了构造和析构,没有显式定义拷贝构造,这时先创建了一个s1,当使用拷贝构造创建s2时,由于没有显式定义,编译器自动生成的实际上是直接将s1中str的值直接复制给了s2中的str,那这样就导致两个指针指向了同一块内存空间。当test函数退出时,先释放s2,调用析构函数,可以成功将s2中str指向的内存空间释放掉,接着释放s1,这时调用析构函数进行释放时,s1中的str已经是野指针了,释放野指针就会报错。
因此当前代码奔溃的根本原因就是浅拷贝
浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。
要想实现深拷贝,我们要从逻辑上知道如何做
每通过拷贝构造创建一个新的string对象的时候,都需要给新创建的对象开辟空间,将原对象中指向空间的内容拷贝到新空间,让原对象和新对象都拥有各自的空间,这样就可以实现深拷贝。
构造/析构函数
注意:在构造时需要进行资源管理,析构时需要进行资源释放
class String
{
public:
String(const char* str = "")
{
if (str == nullptr)
str = "";
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
~String()
{
if (_str)
{
delete[] _str;
_str = nullptr;
}
}
private:
char* _str;
};
拷贝构造/赋值运算符重载的多种写法
注意:进行拷贝构造/赋值运算符重载时,有多种写法,主要分为传统版和现代版,为了实现深拷贝,实际上要让新创建的string对象拥有各自的空间。文章来源:https://www.toymoban.com/news/detail-426066.html
//传统版:代码易懂,但是代码重复率高
String(const String& s)
:_str(new char[strlen(s._str)+1])// 给当前对象的指针变量new一段空间,+1是为了存放'\0'
{
// 将s中指向空间的内容拷贝到当前对象,当前对象就拥有了和s相同内容的空间,但是两份空间是独立的
strcpy(_str, s._str);
}
String& operator=(const String& s)
{
//需要判断是否为自己给自己赋值
if (this != &s)
{
//先创建一个临时指针,空间大小和s中指针指向的空间一致
char* tmp = new char[strlen(s._str) + 1];
//将其内容拷贝至tmp中
strcpy(tmp, s._str);
//释放旧空间
delete[] _str;
//将指针指向修改为新指向
_str = tmp;
}
return *this;
}
现代版的写法,省去了重复的new和strcpy,而是直接使用swap,交换两个指针的指向即可,临时对象在函数结束时调用析构函数释放资源,将临时对象原来指向的空间返回给需要空间的对象。文章来源地址https://www.toymoban.com/news/detail-426066.html
//现代版:代码简洁,但是不易理解
String(const String& s)
:_str(nullptr)//后续需要将tmp中指针指向改为nullptr,调用析构函数释放时就不做任何操作了
{
//用构造函数构造一个临时对象
String tmp(s._str);
//将当前对象中的指针指向和临时对象中指针指向交换,当前对象就拥有和s中指针指向相同内容的空间,
//同时,tmp中指针的指向是nullptr,调用析构函数释放时也不需要做任何操作
swap(_str, tmp._str);
}
// 传址方式的赋值运算符重载
String& operator=(const String& s)
{
if (this != &s)
{
String tmp(s._str);
//调用析构函数时,_str释放的是原来tmp的空间,tmp释放的是原来_str的旧空间
swap(_str, tmp._str);
}
return *this;
}
// 传值方式的赋值运算符重载
String& operator=(String s)
{
//以传值方式传递时,s是原来s的临时拷贝,可以直接通过交换的方式,
//让_str获得临时s指针指向的内容,临时s释放的是原来_str的旧空间
swap(_str, s._str);
return *this;
}
到了这里,关于【C++】string类的深浅拷贝问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!