|
|
|
|
//
|
|
|
|
|
// Created by lenfrex on 2022/4/4.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#include <cstring>
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <cassert>
|
|
|
|
|
|
|
|
|
|
#include "String.h"
|
|
|
|
|
#include "utility"
|
|
|
|
|
|
|
|
|
|
using namespace std::rel_ops;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 按照给定大小来初始化空的字符串数据
|
|
|
|
|
*/
|
|
|
|
|
String::String(int size) : length(0), allocSize(size) {
|
|
|
|
|
data = new char[size]();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 不设定初始化大小就默认128个字符空间来初始化
|
|
|
|
|
*/
|
|
|
|
|
String::String() : String(128) {}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 从源中复制字符串来构建新的对象
|
|
|
|
|
*/
|
|
|
|
|
String::String(const char *source) : String(source, (int) strlen(source)) {}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 从源中复制字符串来构建新的对象,并定义字符串实际长度,小于源字符串则会被截取
|
|
|
|
|
*/
|
|
|
|
|
String::String(const char *source, int length) : String(length + 1) {
|
|
|
|
|
strncpy(data, source, length);
|
|
|
|
|
data[length] = '\0';
|
|
|
|
|
|
|
|
|
|
this->length = length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String::String(const String &source) : String(source.data, source.length) {}
|
|
|
|
|
|
|
|
|
|
String::~String() {
|
|
|
|
|
delete[] data;
|
|
|
|
|
data = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 重载等号,其实现抽象为equals函数
|
|
|
|
|
*/
|
|
|
|
|
bool String::operator==(const String &source) const {
|
|
|
|
|
return equals(source.data, source.length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool String::operator==(const char *source) const {
|
|
|
|
|
return equals(source, (int) strlen(source));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 字符串判等
|
|
|
|
|
* 首先判断data是否为nullptr,同为null则true,否则就是false
|
|
|
|
|
* 再比较两字符串长度,如果长度不一样不用说,直接返回false;
|
|
|
|
|
* 相同的话再比较两字符串数据指针指向地址是否一样,一样就代表指向的是同一块内存,直接返回true
|
|
|
|
|
* 两者都不同才进行逐字比较
|
|
|
|
|
*/
|
|
|
|
|
bool String::equals(const char *source, int sourceLength) const {
|
|
|
|
|
// 首先判断两CString对象data成员是否为空指针
|
|
|
|
|
if (source == nullptr || this->data == nullptr) {
|
|
|
|
|
return source == nullptr && this->data == nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 字符串长度不等,字符串肯定是不等
|
|
|
|
|
if (length != sourceLength) {
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
// 长度相等且data指向相同肯定等
|
|
|
|
|
if (source == this->data) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return strcmp(this->data, source) == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 重载等号运算符,具体实现为copyData()函数
|
|
|
|
|
*/
|
|
|
|
|
String &String::operator=(const String &source) {
|
|
|
|
|
if (this == &source) {
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
copyData(source.data, source.length);
|
|
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String &String::operator=(const char *source) {
|
|
|
|
|
if (source == nullptr) {
|
|
|
|
|
delete[] data;
|
|
|
|
|
this->data = nullptr;
|
|
|
|
|
length = 0;
|
|
|
|
|
allocSize = 0;
|
|
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
copyData(source, (int) strlen(source));
|
|
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void String::copyData(const char *source, int sourceLength) {
|
|
|
|
|
// 等号右侧空指针的话直接data赋为空指针
|
|
|
|
|
if (source == nullptr) {
|
|
|
|
|
delete[] data;
|
|
|
|
|
this->data = nullptr;
|
|
|
|
|
length = 0;
|
|
|
|
|
allocSize = 0;
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果源字符串长度超过了本对象已分配的字符数组大小则重新分配长度合适的字符数组
|
|
|
|
|
if (allocSize <= sourceLength) {
|
|
|
|
|
allocSize = sourceLength + 1;
|
|
|
|
|
|
|
|
|
|
delete[] data;
|
|
|
|
|
data = new char[allocSize];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
strcpy(data, source);
|
|
|
|
|
length = sourceLength;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String &String::operator+=(const String &source) {
|
|
|
|
|
addString(source.data, source.length);
|
|
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String &String::operator+=(const char *source) {
|
|
|
|
|
addString(source, (int) strlen(source));
|
|
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void String::addString(const char *source, int sourceLength) {
|
|
|
|
|
// nullptr不做处理
|
|
|
|
|
if (source == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 本对象nullptr的话就直接新分配
|
|
|
|
|
if (data == nullptr) {
|
|
|
|
|
allocSize = sourceLength + 1;
|
|
|
|
|
this->data = new char[allocSize]();
|
|
|
|
|
|
|
|
|
|
length = sourceLength;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果拼接后字符长度超过已分配的内存大小,则重新分配data内存大小
|
|
|
|
|
if (allocSize <= (length + sourceLength + 1)) {
|
|
|
|
|
// 保存拼接前的原数据,以便重新分配足够的内存
|
|
|
|
|
char *old = new char[length + 1]();
|
|
|
|
|
strcpy(old, data);
|
|
|
|
|
|
|
|
|
|
// 重新分配内存,防止拼接后溢出
|
|
|
|
|
// 分配两倍内存空间来减少重新分配次数(空间换时间?)
|
|
|
|
|
allocSize = (length + sourceLength)*2 + 1;
|
|
|
|
|
delete[] data;
|
|
|
|
|
this->data = new char[allocSize]();
|
|
|
|
|
|
|
|
|
|
strcpy(data, old);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
strcat(data, source);
|
|
|
|
|
length += sourceLength;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 按照字典顺序对比
|
|
|
|
|
*/
|
|
|
|
|
bool String::operator>(const String &compareString) const {
|
|
|
|
|
// 首先判断两CString对象data成员是否为空指针,空直接返回false
|
|
|
|
|
if (compareString.data == nullptr || this->data == nullptr) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 两对象data成员首指向相同则对比谁长
|
|
|
|
|
if (compareString.data == this->data) {
|
|
|
|
|
return length > compareString.length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 以上情况都不符合时才进行比较
|
|
|
|
|
return strcoll(this->data, compareString.data) > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::ostream &operator<<(std::ostream &outputStream, const String &source) {
|
|
|
|
|
return outputStream << source.data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::istream &operator>>(std::istream &inputStream, String &source) {
|
|
|
|
|
// 其实这里有个问题,如果用户的输入超过了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) {
|
|
|
|
|
// 用断言处理下标越界,一般应该用异常处理,但是简单起见还是断言吧
|
|
|
|
|
assert(i < length);
|
|
|
|
|
return data[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 加号运算重载,每次加号运算都会产生一个新的对象
|
|
|
|
|
* 这里不用引用返回是因为返回局部对象不是安全的
|
|
|
|
|
*/
|
|
|
|
|
String String::operator+(const char *source) {
|
|
|
|
|
int sourceLength = (int) strlen(source);
|
|
|
|
|
|
|
|
|
|
String target = String((this->length + sourceLength)*2 + 1);
|
|
|
|
|
target = this->data;
|
|
|
|
|
target += source;
|
|
|
|
|
|
|
|
|
|
return target;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String String::operator+(const String &source) {
|
|
|
|
|
String target = String((this->length + source.length)*2 + 1);
|
|
|
|
|
target = this->data;
|
|
|
|
|
target += source;
|
|
|
|
|
|
|
|
|
|
return target;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int String::Length() const {
|
|
|
|
|
return length;
|
|
|
|
|
}
|