实现C++自定义的String类

一、简介

  • 采用了COW写时复制的方式实现,即在每个String类数据域之前用了4个字节的空间进行引用计数。通过拷贝构造函数或者赋值运算符函数进行赋值时不会重新开辟空间,只会对引用计数加一,当有修改操作时,才会重新开辟新的空间;
  • 内部定义了一个char类型的内部代理类,用于解决String类重载下标运算符后无法区分读写操作的问题。

二、头文件

#pragma once
#include <iostream>


class String {
	class CharProxy { // char类型的代理内部类
	public:
		CharProxy(int index, String &str);
		char &operator=(const char &c);
		friend std::ostream &operator<<(std::ostream &os, const CharProxy &charPorxy);
	private:
		int _index;	   // 下标
		String &_this; // 指向外部类对象
	};

public:
	String();
	String(const char *pstr);
	String(const String &str);
	~String();
	String &operator=(const String &str);
	String &operator=(const char *pstr);

	String &operator+=(const String &str);
	String &operator+=(const char *pstr);
	
	String::CharProxy operator[](std::size_t index);
	
	std::size_t size()const; // 字符串的长度
	const char* c_str()const; // 返回一个C风格的字符串
	
	friend bool operator==(const String &leftStr, const String &rightStr);
	friend bool operator!=(const String &leftStr, const String &rightStr);
	
	friend bool operator<(const String &leftStr, const String &rightStr);
	friend bool operator>(const String &leftStr, const String &rightStr);
	friend bool operator<=(const String &leftStr, const String &rightStr);
	friend bool operator>=(const String &leftStr, const String &rightStr);
	
	friend std::ostream &operator<<(std::ostream &os, const String &str);
    friend std::istream &operator>>(std::istream &is, String &str);

	int refCount(); // 返回引用计数

	friend std::ostream &operator<<(std::ostream &os, const CharProxy &charPorxy);
private:
	char *malloc(const char *pstr = nullptr); // 为字符串申请空间
	void release(); // 释放字符串的空间
	void initRefCount(); // 初始化引用计数
	void increaseRefCount(); //增加引用计数
	void decreaseRefCount(); // 减少引用计数
	static const int refCountLength = 4; // 引用计数所占字节数
	char * _pstr;
};

String operator+(const String &thisStr, const String &otherStr);
String operator+(const String &thisStr, const char *otherpStr);
String operator+(const char *thisPstr, const String &otherStr);

三、String实现

#include <cstring>
#include "08_MyString.hh"

using namespace std;

/**
 * 内部代理类有参构造函数 
*/
String::CharProxy::CharProxy(int index, String &str) :_index(index), _this(str) {
	cout << "CharProxy()" << endl;
}

/**
 * 重载内部代理类的赋值运算符=
*/
char &String::CharProxy::operator=(const char &c) {	
	cout << "char &operator=(const char &c)" << endl;
	if (_index < strlen(_this._pstr)) { // 未越界
		if (*(int*)(_this._pstr - refCountLength) > 1) { // 判断引用计数是否大于1
			_this.decreaseRefCount(); // 原空间引用计数-1
			char *newStr = _this.malloc(_this._pstr); // 开辟新空间
			strcpy(newStr, _this._pstr);				
			_this._pstr = newStr;
			_this.initRefCount(); // 初始化引用计数(此处时机一定要在赋值了新串之后)		
		}
		_this._pstr[_index] = c; // 修改元素
		return _this._pstr[_index];
	}else {
		static char nullchar = '\0';
		return nullchar;
	}	
}

/**
 * 重载内部代理类的输出运算符<<
*/
ostream &operator<<(std::ostream &os, const String::CharProxy &charPorxy) {
	if (charPorxy._index < strlen(charPorxy._this._pstr)) {
		os << charPorxy._this._pstr[charPorxy._index];
	}else {
		os << "out of range";
	}
	return os;
}

/**
 * 为字符串申请空间 
 * 初始化并偏移到数据的位置
*/
char *String::malloc(const char *pstr) { 
	if (pstr == nullptr) {
		return new char[1 + refCountLength]() + refCountLength; 
	} else {
		return new char[strlen(pstr) + 1 + refCountLength]() + refCountLength;
	}
}

/**
 * 释放字符串的空间
*/
void String::release() {
	decreaseRefCount();
	if (refCount() == 0) {
		delete [] (_pstr - refCountLength);
		_pstr = nullptr;
		cout << "release()......." << endl;
	}
}

/**
 * 初始化引用计数
*/
void String::initRefCount() {
	*(int*)(_pstr - refCountLength) = 1;
}

/**
 * 增加引用计数
*/
void String::increaseRefCount(){
	++*(int*)(_pstr - refCountLength);
}
/**
 * 减少引用计数
*/
void String::decreaseRefCount(){
	--*(int*)(_pstr - refCountLength);
}
/**
 * 返回引用计数
*/
int String::refCount() {
	return *(int*)(_pstr - refCountLength);
}

/**
 * 无参构造函数
*/
String::String()
: _pstr(malloc())
{
	initRefCount();
	strcpy(_pstr, "");	
	cout << "String()" << endl;
}

/**
 * 有参构造函数
*/
String::String(const char *pstr) 
: _pstr(malloc(pstr)) 
{
	initRefCount();
	strcpy(_pstr, pstr);
	cout << "String(const char *pstr) " << endl;
}

/**
 * 拷贝构造函数
*/
String::String(const String &str)
: _pstr(str._pstr) // 浅拷贝
{
	increaseRefCount();
	cout << "String(const String &str)" << endl;
}

/**
 * 析构函数
*/
String::~String() {
	release();
}

/**
 * 重载赋值运算符函数
*/
String &String::operator=(const String &str) {
	if (this != &str && strcmp(_pstr, str._pstr) != 0) { // 跳过自赋值 和相同字符串
		release();
		_pstr = str._pstr; // 浅拷贝	
		increaseRefCount();
	} 
	return *this; 
}
String &String::operator=(const char *pstr) {
	if (strcmp(_pstr, pstr) != 0) { // 跳过相同字符串
		release(); // 删除旧字符串
		_pstr = malloc(pstr); // 申请新空间 
		initRefCount(); // 初始化引用计数
		strcpy(_pstr, pstr); // 复制	
	}
	return *this;
}

/**
 * 重载+=
*/
String &String::operator+= (const String &str) {
	if (str._pstr != nullptr && strlen(str._pstr) > 0) {
		size_t newLen = strlen(_pstr) + strlen(str._pstr) + refCountLength + 1;
		char *newStr = new char[newLen]() + refCountLength; // 偏移到数据的位置
		strcat(newStr, _pstr);
		strcat(newStr, str._pstr);
		release(); // 删除旧字符串
		_pstr = newStr;
		initRefCount(); // 初始化引用计数(此处时机一定要在赋值了新串之后)
	}
	return *this;
}
String &String::operator+= (const char *pstr) {
	if (pstr != nullptr && strlen(pstr) > 0) {
		size_t newLen = strlen(_pstr) + strlen(pstr) + refCountLength + 1;
		char *newStr = new char[newLen]() + refCountLength; // 偏移到数据的位置
		strcat(newStr, _pstr);
		strcat(newStr, pstr);
		release(); // 删除旧字符串
		_pstr = newStr;
		initRefCount(); // 初始化引用计数(此处时机一定要在赋值了新串之后)
	}
	return *this;
}

/**
 * 重载下标运算符
*/
String::CharProxy String::operator[] (size_t index) {
	return String::CharProxy(index, *this);
}

/**
 * 获取字符串长度
*/
size_t String::size() const {
	return strlen(_pstr);
}

/**
 * 返回C风格字符串
*/
const char *String::c_str() const {
	return _pstr;
}

/**
 * 重载==
*/
bool operator==(const String &leftStr, const String &rightStr) {
	return strcmp(leftStr._pstr, rightStr._pstr) == 0 ? true : false;
}
/**
 * 重载!=
*/
bool operator!=(const String &leftStr, const String &rightStr) {
	return strcmp(leftStr._pstr, rightStr._pstr) != 0 ? true : false;
}
/**
 * 重载<
*/
bool operator<(const String &leftStr, const String &rightStr) {
	return strcmp(leftStr._pstr, rightStr._pstr) < 0 ? true : false;
}
/**
 * 重载>
*/
bool operator>(const String &leftStr, const String &rightStr) {
	return strcmp(leftStr._pstr, rightStr._pstr) > 0 ? true : false;
}
/**
 * 重载<=
*/
bool operator<=(const String &leftStr, const String &rightStr) {
	int res = strcmp(leftStr._pstr, rightStr._pstr); 
	return res > 0 ? false : true;
}
/**
 * 重载>=
*/
bool operator>=(const String &leftStr, const String &rightStr) {
	int res = strcmp(leftStr._pstr, rightStr._pstr); 
	return res < 0 ? false : true;
}

/**
 * 重载输出流函数
*/
ostream &operator<< (ostream &os, const String &str) {
	os << str._pstr;
	return os;
}

/**
 * 重载输入流函数
*/
istream &operator>> (istream &is, String &str) {
	if (strlen(str._pstr) > 0) {
		str = ""; // str非空就先清空str
	}
	char buf[1024];
	while (1) {
		is.clear(); // 恢复流的状态
		bzero(buf, sizeof(buf)); // 清空buf
		is.get(buf, sizeof(buf)); // 读取一个buf的大小
		int len = strlen(buf);
		if (len == 0) { // buf长度为0时退出循环 
			break;
		}	
		str += buf;
	}
	return is;
}

/**
 * 重载+
*/
String operator+(const String &thisStr, const String &otherStr) {
	if (strlen(thisStr.c_str()) == 0) {
		return otherStr;
	}
	if (strlen(otherStr.c_str()) == 0) {
		return thisStr;
	}
	String temp(thisStr);
	temp+=otherStr;
	return temp;
}
String operator+(const String &thisStr, const char *otherPstr) {
	if (strlen(thisStr.c_str()) == 0) {
		return String(otherPstr);
	}
	if (strlen(otherPstr) == 0) {
		return thisStr;
	}
	String temp(thisStr);
	temp+=otherPstr;
	return temp;
}
String operator+(const char *thisPstr, const String &otherStr) {
	if (strlen(thisPstr) == 0) {
		return otherStr;
	}
	if (strlen(otherStr.c_str()) == 0) {
		return String(thisPstr);
	}
	String temp(thisPstr);
	temp+=otherStr;
	return temp;
}

相关推荐

  1. 实现C++定义String

    2024-03-18 23:16:01       34 阅读
  2. Spring Task 定义定时任务

    2024-03-18 23:16:01       50 阅读
  3. C#定义异常(Exception)实现

    2024-03-18 23:16:01       29 阅读
  4. C# Newtonsoft.Json解析json到定义实体

    2024-03-18 23:16:01       54 阅读
  5. C++学习——模板使用:定义数组

    2024-03-18 23:16:01       56 阅读
  6. C#使用定义方法设计堆栈

    2024-03-18 23:16:01       45 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-18 23:16:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-18 23:16:01       100 阅读
  3. 在Django里面运行非项目文件

    2024-03-18 23:16:01       82 阅读
  4. Python语言-面向对象

    2024-03-18 23:16:01       91 阅读

热门阅读

  1. Git详细入门笔记

    2024-03-18 23:16:01       28 阅读
  2. 2022 Task 2 Max Sum of 2 integers sharing first and last digits

    2024-03-18 23:16:01       38 阅读
  3. 归并排序思路

    2024-03-18 23:16:01       36 阅读
  4. 职场人如何看待领导打绩效

    2024-03-18 23:16:01       36 阅读
  5. Winform编程详解十四:NumericUpDown 数字输入框

    2024-03-18 23:16:01       42 阅读
  6. 使用Cloudflare来给wordpress网站图片自动压缩加速

    2024-03-18 23:16:01       36 阅读
  7. nslookup和dig命令的使用方法以及区别

    2024-03-18 23:16:01       36 阅读
  8. 使用wx:for()

    2024-03-18 23:16:01       44 阅读
  9. 前缀和--k倍区间

    2024-03-18 23:16:01       37 阅读
  10. 中文编程入门(Lua5.4.6中文版)第五章 Lua 函数

    2024-03-18 23:16:01       40 阅读
  11. 从零开始学HCIA之网络自动化01

    2024-03-18 23:16:01       40 阅读