C++ string类的实现
string
一、类的设计与思路
根据文件之中提供的 main() 函数接口,在类的设计中,一共需要实现以下功能:
-
构造函数
-
拷贝构造
-
拷贝赋值
-
+ 和 << 的操作符重载
由于
String的数据类型实质是char*指针,所以为了防止出现浅拷贝的情况,拷贝构造和拷贝赋值函数我们不能使用 default类型,而需要自己进行重载。 由于拷贝这一功能需要在函数中进行反复调用,所以手动实现
strcpy()函数的功能作为一个 public 函数在函数中被调用以减少代码量。 为了防止在 copy 的过程中出现地址覆盖的情况,编写
Memcpy函数,实现更安全的拷贝字符串。 编写
Strlen()函数用于计算字符串长度,被其他函数具体调用,会使代码更加简洁。 成员函数重载 + 操作符
友元函数重载 << 输出操作符
class String
{
public:
String(const char* = nullptr);
String(const String& );
String& operator=(const String&);//设置为成员函数
~String();
String operator+(const String&);
friend std::ostream& operator<<(std::ostream&, const String&);//友元重载
unsigned int Strlen(const char* = nullptr);
char* Memcpy(char*, const char*, int);//检查内存的重叠问题
char *Strcpy(char *, const char *);
private:
char* m_data;
};
二、具体各函数实现
1.Constructor
1.1.思路
根据main() 接口,构造函数的参数应为 const char* 类型,默认为 nullptr,参数传入之后,首先调用 Strlen 计算并开辟指定长度的空间,之后调用 Strcpy 复制到开辟的空间之。假设传入的是 nullptr (默认参数),就开辟一块长度为1的空间,储存‘\0’。
1.2.代码实现
inline
String::String(const char*cstr)
{
if (cstr) {
m_data = new char[Strlen(cstr) + 1];
Strcpy(m_data, cstr);
}
else {
m_data = new char[1];
*m_data = '\0';
}
}
2.Copy Constructor
2.1.思路
显式的重载之,参数为 String 类型,为了提高效率,引用传递,并使用 const 限定符修饰。和默认 copy ctor 相比(单纯地复制指针),只需要开辟一块新的空间复制以下对应的内容即可。
2.2.代码实现
inline
String::String(const String&str) {
m_data = new char[Strlen(str.m_data) + 1];
Strcpy(m_data, str.m_data);
}
3.Copy Assignment Operator
3.1.思路
采用成员函数实现的形式:回收左指针指向的空间,根据右值的大小开辟一块新的同样大小的空间,调用 Strcpy 实现赋值。
3.2.代码实现
inline
String& String::operator=(const String& str)
{
//检测自我赋值以提高效率
if (this == &str)
return *this;
delete[]m_data;//回收空间
m_data = new char[Strlen(str.m_data) + 1];//开辟空间
Strcpy(m_data, str.m_data);//赋值
return *this;
}
4.Strlen()
4.1.思路
创建一个临时指针指向数组头地址,利用内存连续,向后探查至尾地址,做差,利用char长度为一个字节计算出数组长度。由于不可能为负数,返回一个 unsigned int类型
4.2.代码实现
inline
unsigned int String::Strlen(const char* cstr)
{
const char*temp = cstr;
while (*temp++);
return(temp - cstr - 1);
}
5.Strcpy() && Memcpy()
5.1.思路
核心就是两个参数:char* dst和 char*src,我们编写 Strcpy() 的目的就是在以为首地址的空间中将空间为 src ~ src+strlen(src)中存储的内容复制过去。这里,我们需要考虑到的是,假设我们从低地址向高地址进行复制,是否会出现地址重叠的情况。因此,加上一个地址重叠的判断。并且为了实现链式表达,我们设置了指针类型的返回值,返回的是 dst 。
5.2.代码实现
inline
char* String::Strcpy(char *dst, const char *src)
{
if (dst == nullptr || src == nullptr)
return nullptr;
char *ret = dst;
if (Memcpy(dst, src, Strlen(src) + 1))//此函数内实现copy
return ret;
else
return nullptr;
}
//cnt是源字符串的长度,利用内存连续的特点计算是否出现重叠
inline
char* String::Memcpy(char *dst, const char* src, int cnt)
{
//加一个类似于断言assert
if (dst == nullptr || src == nullptr)
return nullptr;
char *ret = dst;
if (dst >= src && dst <= src + cnt - 1) //内存重叠,从高地址开始复制
{
dst = dst + cnt - 1;
src = src + cnt - 1;
while (cnt--)
*dst-- = *src--;
}
else //正常情况,从低地址开始复制
{
while (cnt--)
*dst++ = *src++;
}
return ret;
}
6.operator << override
6.1.思路
设置为友元,可以直接调用 m_data(private数据) ,而在我们的类设计之中,char[]的最后一定是 ‘\0’,所以只需要直接输出即可,并返回 ostream& 以实现链式输出。
6.2.代码实现
inline
std::ostream& operator<<(std::ostream& out, const String&str)
{
out << str.m_data;
return out;
}
7.operator + override
7.1.思路
设置为成员函数,实现拼接,所以就是返回一个新的 String 类型的对象,它的长度是左操作数和右操作数的长度之和,调用两次 Strcpy 实现赋值即可。
7.2.代码实现
inline
String String::operator+(const String&str)
{
String res;
res.m_data = new char[Strlen(m_data) + Strlen(str.m_data) + 1];
//实现拼接
Strcpy(res.m_data, m_data);
Strcpy(res.m_data + Strlen(res.m_data), str.m_data);
res.m_data[Strlen(m_data) + Strlen(str.m_data)] = '\0';
return res;
}
8.Deconstructor
8.1.思路
没什么好说的,就...,把对应的 String 对象的private里面的动态数组删除了就好。
8.2.代码实现
inline
String::~String() {
delete[]m_data;
m_data = nullptr;
}
三、程序运行截图


浙公网安备 33010602011771号