|
|
|
@ -4,8 +4,12 @@ |
|
|
|
|
|
|
|
|
|
#include <cstring> |
|
|
|
|
#include <algorithm> |
|
|
|
|
#include <cassert> |
|
|
|
|
|
|
|
|
|
#include "String.h" |
|
|
|
|
#include "utility" |
|
|
|
|
|
|
|
|
|
using namespace std::rel_ops; |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 按照给定大小来初始化空的字符串数据 |
|
|
|
@ -15,7 +19,7 @@ String::String(int size) : length(0), allocSize(size) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 不设定初始化大小就默认128个字符来初始化 |
|
|
|
|
* 不设定初始化大小就默认128个字符空间来初始化 |
|
|
|
|
*/ |
|
|
|
|
String::String() : String(128) {} |
|
|
|
|
|
|
|
|
@ -34,17 +38,15 @@ String::String(const char *source, int length) : String(length + 1) { |
|
|
|
|
this->length = length; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
String::String(const String &source) : String(source.data, source.length) {} |
|
|
|
|
|
|
|
|
|
String::~String() { |
|
|
|
|
delete[] data; |
|
|
|
|
data = nullptr; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
String::String(const String &source) : String(source.data, source.length) {} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 重载等号,其实现抽象为equals函数 |
|
|
|
|
* @param source |
|
|
|
|
* @return |
|
|
|
|
*/ |
|
|
|
|
bool String::operator==(const String &source) const { |
|
|
|
|
return equals(source.data, source.length); |
|
|
|
@ -55,7 +57,7 @@ bool String::operator==(const char *source) const { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 借鉴了Java中String类的equal()方法的实现思路。 |
|
|
|
|
* 字符串判等 |
|
|
|
|
* 首先判断data是否为nullptr,同为null则true,否则就是false |
|
|
|
|
* 再比较两字符串长度,如果长度不一样不用说,直接返回false; |
|
|
|
|
* 相同的话再比较两字符串数据指针指向地址是否一样,一样就代表指向的是同一块内存,直接返回true |
|
|
|
@ -80,6 +82,9 @@ bool String::equals(const char *source, int sourceLength) const { |
|
|
|
|
return strcmp(this->data, source) == 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 重载等号运算符,具体实现为copyData()函数 |
|
|
|
|
*/ |
|
|
|
|
String &String::operator=(const String &source) { |
|
|
|
|
if (this == &source) { |
|
|
|
|
return *this; |
|
|
|
@ -161,9 +166,9 @@ void String::addString(const char *source, int sourceLength) { |
|
|
|
|
strcpy(old, data); |
|
|
|
|
|
|
|
|
|
// 重新分配内存,防止拼接后溢出
|
|
|
|
|
// 分配两倍内存空间来减少重新分配次数
|
|
|
|
|
delete[] data; |
|
|
|
|
// 分配两倍内存空间来减少重新分配次数(空间换时间?)
|
|
|
|
|
allocSize = (length + sourceLength)*2 + 1; |
|
|
|
|
delete[] data; |
|
|
|
|
this->data = new char[allocSize](); |
|
|
|
|
|
|
|
|
|
strcpy(data, old); |
|
|
|
@ -196,20 +201,30 @@ std::ostream &operator<<(std::ostream &outputStream, const String &source) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::istream &operator>>(std::istream &inputStream, String &source) { |
|
|
|
|
// 使用istream.get(char*, int)可以限制读入的长度,防止输入超长的字符串导致溢出,但是空格也会被读入
|
|
|
|
|
// 手动分割太麻烦,因此只好先用这种方法了
|
|
|
|
|
inputStream >> source.data; |
|
|
|
|
source.length = (int) strlen(source.data); |
|
|
|
|
// 其实这里有个问题,如果用户的输入超过了4028个字符,会导致溢出,不安全
|
|
|
|
|
// 虽然可以像std::string那样分批读入,动态分配,这样过长就不会被截掉,
|
|
|
|
|
// 但是为了简单起见,在这里就直接>>了
|
|
|
|
|
char* input = new char[4028](); |
|
|
|
|
if (std::cin >> input) { |
|
|
|
|
strncpy(source.data, input, source.allocSize - 1); |
|
|
|
|
|
|
|
|
|
int inputLength = (int) strlen(input); |
|
|
|
|
source.length = (int) std::min(inputLength, source.allocSize - 1); |
|
|
|
|
|
|
|
|
|
source.data[source.length] = '\0'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return inputStream; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
char &String::operator[](int i) { |
|
|
|
|
return (i < length && i >= 0) ? data[i] : data[0]; |
|
|
|
|
// 用断言处理下标越界,一般应该用异常处理,但是简单起见还是断言吧
|
|
|
|
|
assert(i < length); |
|
|
|
|
return data[i]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 加号运算重载,注意,每次加号运算都会产生一个新的对象 |
|
|
|
|
* 加号运算重载,每次加号运算都会产生一个新的对象 |
|
|
|
|
* 这里不用引用返回是因为返回局部对象不是安全的 |
|
|
|
|
*/ |
|
|
|
|
String String::operator+(const char *source) { |
|
|
|
|