作为C++从业者,我相信都会被考察过实现简单的string类,包括构造、析构、拷贝构造以及赋值拷贝等,因为这能够很好的考察面试者的C++基本功。借看《剑指offer》的机会将这个问题重新整理一下。具体实现如下:
class string {
public:
string(const char* cstr = nullptr)
{
if (cstr) {
m_data = new char[strlen(cstr) + 1];
strcpy(m_data, cstr);
} else {
m_data = new char[1];
*m_data = '\0';
}
}
string(const string& str)
{
m_data = new char[strlen(str.m_data) + 1];
strcpy(m_data, str.m_data);
}
~string()
{
delete[] m_data;
}
string& operator = (const string& rhs)
{
if (this == &rhs)
return *this;
delete[] m_data;
m_data = new char[strlen(rhs.m_data) + 1];
strcpy(m_data, rhs.m_data);
return *this;
}
friend ostream& operator << (ostream& os, const string& rhs)
{
return os << rhs.m_data;
}
private:
char* m_data;
};
上述代码赋值拷贝将是面试官的考察重点:
上面是实现使用于C++初级程序员,但对于C++高级程序员来说还需要考虑异常安全性。前面的实现中,我们在分配内存之前释放了m_data的内存,如果此时内存不足导致new char抛出异常,m_data将是一个空指针,这样非常容易导致程序崩溃。也就是说一旦在赋值运算符函数内部抛出一个异常,string的实例不再保持有效的状态,这就违背了安全性原则。
想要在赋值运算符函数中实现异常安全性,我们有两种方法。一个简单的方法是先用new分配新内容再释放原来空间,另一个更好的方法是先创建一个临时变量,再交换临时变量和原来的实例。代码实现如下:
string& operator = (const string& rhs)
{
if (this != &rhs)
{
string tmp(rhs);
char* p = tmp.m_data;
tmp.m_data = m_data;
m_data = p;
}
return *this;
}
在上述实现中,先创建一个临时变量tmp,接着将tmp.m_data和自身的m_data交换,由于tmp是一个局部变量,出了作用域就会调用析构函数将内存释放掉。如果临时变量调用构造函数时,由于内存不足抛出bad_alloc等异常,我们还没有修改原来实例的状态,因此实例是有效的,这保证了异常安全性。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。