今天我们要学习的是经典的string类模拟实现问题。这在面试中 面试官也会经常叫你实现一个string类。
所以我们必须掌握其中的知识点和易错点!
//经典的string类模拟实现问题
//我们在模拟string类的时候最重要的就是
//构造函数、拷贝构造函数、赋值重载、析构函数
//这里的String是为了与string区别
class String
{
public:
//构造函数
String(const char* str = "")
{
if (str == nullptr)
{
assert(false);
return;
}
//开辟空间
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
//这里的拷贝构造 和赋值构造是调用系统给的
//析构函数
~String()
{
if (_str)
{
delete[] _str;
_str = nullptr;
}
}
private:
char* _str;
};
//测试
int main()
{
String s1("hello world");
String s2(s1);
}
综上所述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用p1构造p2时,编译器会调用默认的拷贝构造。最终导致的问题是,p1,p2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,就叫做浅拷贝.
浅拷贝:
也称为位拷贝,编译器知识将对象中的值拷贝过来,如果对象中管理资源最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进行操作的时候,就会发生访问违规了。**
深拷贝
如果一个类中涉及到资源的管理,其拷贝构造函数,赋值运算符重载以及析构函数必须要显式给出,一般情况都是按照深拷贝方式提供
传统版写法的String类
//传统版写法的String类
class String
{
public:
//构造函数
String(const char* str = "")
{
if (str == nullptr)
{
assert(false);
return;
}
//开空间
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
//拷贝构造函数
String(const String& s)
:_str(new char[strlen(s._str) + 1])
{
strcpy(_str, s._str);
}
//赋值重载=
String& operator=(const String& s)
{
//判读是否是自赋值
if (this != &s)
{
char* temp = new char[strlen(s._str) + 1];
strcpy(_str, s._str);
delete[] _str;
_str = temp;
}
return *this;
}
//析构函数
~String()
{
if (_str)
{
delete[] _str;
_str = nullptr;
}
}
private:
char* _str;
};
现代版写法的String类
//现代版String类写法
class String
{
public:
//构造
String(const char* str = "")
{
if (str == nullptr)
{
assert(false);
return;
}
//开空间
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
//拷贝构造函数
String(const String& s)
:_str(nullptr)
{
String tmp(s._str);
swap(_str, tmp._str);
}
//赋值重载=
String& operator=(String s)
{
swap(_str, s._str);//两个交换 不同的地址了
return *this;
}
//析构函数
~String()
{
if(_str)
{
delete[] _str;
_str = nullptr;
}
}
private:
char* _str;
};
面试中string类的一种正确写法:
#include <utility>
#include <string.h>
class String
{
public:
String()
: data_(new char[1])
{
*data_ = '\0';
}
String(const char* str)
: data_(new char[strlen(str) + 1])
{
strcpy(data_, str);
}
String(const String& rhs)
: data_(new char[rhs.size() + 1])
{
strcpy(data_, rhs.c_str());
}
/* Delegate constructor in C++11
String(const String& rhs)
: String(rhs.data_)
{
}
*/
~String()
{
delete[] data_;
}
/* Traditional:
String& operator=(const String& rhs)
{
String tmp(rhs);
swap(tmp);
return *this;
}
*/
String& operator=(String rhs) // yes, pass-by-value
{
swap(rhs);
return *this;
}
// C++ 11
String(String&& rhs)
: data_(rhs.data_)
{
rhs.data_ = nullptr;
}
String& operator=(String&& rhs)
{
swap(rhs);
return *this;
}
// Accessors
size_t size() const
{
return strlen(data_);
}
const char* c_str() const
{
return data_;
}
void swap(String& rhs)
{
std::swap(data_, rhs.data_);
}
private:
char* data_;
};
如有错误,多多指教!