实现一个自己的String类是一道考验C++基础知识的好题。能够准确无误地编写出String类的构造函数、拷贝构造函数、赋值函数和析构函数的面试者至少已经具备了C++基本功的60%以上!
在这个类中包括了指针类成员变量m_pData,当类中包括指针类成员变量时,一定要重载其拷贝构造函数、赋值函数和析构函数,这既是对C++程序员的基本要求,也是《Effective C++》中特别强调的条款。
仔细学习这个类,特别注意加注释的得分点和加分点的意义,这样就具备了60%以上的C++基本功!
至少要能实现以下:构造函数,析构函数,拷贝构造函数(copy constructor),重载赋值操作符(copy assignment operator)。
String.h代码如下,
#pragma once
#pragma warning(disable:4996)
#include<iostream>
using namespace std;
class String
{
public:
String();
String(const char* pStr);
String(const String& rhs);
String& operator=(const String& rhs);
~String();
char operator[](int i) const;
int size() const;
char* c_str() const;
private:
char* m_pData;
friend ostream& operator<<(ostream& out, String& s);
friend istream& operator>>(istream& in, String& s);
};
String.cpp代码如下,
#include "String.h"
String::String() : m_pData(new char[1])
{
*m_pData = '\0';
}
String::String(const char * pStr)
{
if (!pStr)
{
m_pData = new char[1];
*m_pData = '\0';
}
else
{
int nLen = strlen(pStr);
m_pData = new char[nLen + 1];
strcpy(m_pData, pStr);
}
}
String::String(const String & rhs)
{
int nLen = strlen(rhs.m_pData);
m_pData = new char[nLen + 1];
strcpy(m_pData, rhs.m_pData);
}
String & String::operator=(const String & rhs)
{
if (&rhs != this)
{
if(m_pData)
delete[] m_pData;
int nLen = strlen(rhs.m_pData);
m_pData = new char[nLen + 1];
strcpy(m_pData, rhs.m_pData);
}
return *this;
}
String::~String()
{
if (m_pData)
{
delete[] m_pData;
}
}
char String::operator[](int i) const
{
int nLen = strlen(m_pData) - 1;
if (i > nLen)
{
return '\0';
}
else
{
return m_pData[i];
}
}
int String::size() const
{
return strlen(m_pData);
}
char * String::c_str() const
{
return m_pData;
}
ostream & operator<<(ostream & out, String & s)
{
out << s.m_pData;
return out;
}
istream & operator >> (istream & in, String & s)
{
// TODO: insert return statement here
char p[50];
in.getline(p, 50);
s = p;
return in;
}
main.cpp代码如下,
#include "String.h"
int main(int argc, char** argv)
{
String str3;
String str1 = "man";
String str2("shou");
char ch = str1[2];
//str1[2] = 'p';
str3 = str1;
cout << ch << endl;
cout << str1 << endl;
cout << str2 << endl;
cout << str3 << endl;
system("pause");
return 0;
}
注意,String类的实现是用的深拷贝,如果用浅拷贝,当释放空间会把有用的空间释放掉,因为每次函数完成后会调用析构函数。
- 深拷贝,给要拷贝构造的对象重新分配空间。
- 浅拷贝,是对对象的简单拷贝,让几个指针都指向同一块地址空间,在释放这段空间的时候会产生“对已释放的空间再次释放”,导致程序中断。
上面是老版本的拷贝构造函数和重载赋值操作符的实现方式。有几点需要注意,判断自己赋值给自己 和 异常安全性。老版本的拷贝构造函数的实现,new的时候有可能会抛出异常。现代方法是通过使用
swap函数
简化方法。
使用swap的拷贝构造函数,通过swap将临时变量rhs中的数据保存到了data_中,同时data_中的数据拷贝到了临时变量中,在函数返回时会被自动释放。
代码修改如下,
String::String(const String & rhs)
{
/*int nLen = strlen(rhs.m_pData);
m_pData = new char[nLen + 1];
strcpy(m_pData, rhs.m_pData);*/
std::swap(m_pData, rhs.m_pData);
}
String & String::operator=(const String & rhs)
{
/*if (&rhs != this)
{
if(m_pData)
delete[] m_pData;
int nLen = strlen(rhs.m_pData);
m_pData = new char[nLen + 1];
strcpy(m_pData, rhs.m_pData);
}*/
std::swap(m_pData, rhs.m_pData);
return *this;
}
版权声明:本文为sinat_34715587原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。