C++模拟实现string类

序言:

        在本文中,我们将基于C++实现string类中的一些基本功能,并且完成string类功能的测试。

一、基本框架

        1.在实现string之前,我们需要创建三个文件:string.h用于存放类的声明,string.cpp来实现类的基本.功能,test.cpp来对模拟类进行测试。

        2.为了避免与std中的string类冲突,我们模拟的string类应在我们定义的命名空间mystring中去声明定义。

         3.string类的成员变量应该包括字符串_str,容量_capacity,大小_size。

二、类的声明

以下是我们需要模拟实现string类的结构以及一些功能:

#define _CRT_SECURE_NO_WARNINGS 1
#include <bits/stdc++.h>
using namespace std;


namespace mystring {
    class string {
        friend ostream& operator<<(ostream& _cout, const mystring::string& s);

        friend istream& operator>>(istream& _cin, mystring::string& s);

    public:

        typedef char* iterator;

        //构造与析构
        string(const char* str = "");

        //拷贝构造
        string(const string& s);

        //赋值重载
        string& operator=(const string& s);

        ~string();

        //迭代器函数

        iterator begin();

        iterator end();

        const iterator begin()const;

        const iterator end()const;

        // 修改函数

        void push_back(char c);

        string& operator+=(char c);

        void append(const char* str);

        string& operator+=(const char* str);

        void clear();

        void swap(string& s);

        const char* c_str()const;

        // 容量操作函数

        size_t size()const;

        size_t capacity()const;

        bool empty()const;

        void resize(size_t n, char c = '\0');

        void reserve(size_t n);

        //访问函数

        char& operator[](size_t index);

        const char& operator[](size_t index)const;


        //关系运算符重载

        bool operator<(const string& s);

        bool operator<=(const string& s);

        bool operator>(const string& s);

        bool operator>=(const string& s);

        bool operator==(const string& s);

        bool operator!=(const string& s);



        // 返回c在string中第一次出现的位置

        size_t find(char c, size_t pos = 0) const;

        // 返回子串s在string中第一次出现的位置

        size_t find(const char* s, size_t pos = 0) const;

        // 在pos位置上插入字符c/字符串str,并返回该字符的位置

        string& insert(size_t pos, char c);

        string& insert(size_t pos, const char* str);


        // 删除pos位置上的元素,并返回该元素的下一个位置

        string& erase(size_t pos, size_t len);

    };
}

三、构造与析构

我们需要实现构造函数,拷贝构造,赋值重载,析构函数共四个函数。

1.构造函数

string(const char* str = "") 
	: _size(strlen(str))
	, _str(new char[strlen(str) + 1])
	, _capacity(_size)
{
	strcpy(this->_str, str);
}

考虑到在创建对象的时候,可能不会指定对象内容,所以我们使用全缺省函数进行构造,默认内容为空。再初始化其_size和_capacity;

2.拷贝构造函数

string(const string& s)
	: _str(new char[s.size()+1])
	,_size(s.size())
	,_capacity(_size){
	strcpy(this->_str, s._str);
}

 3.赋值重载

//赋值重载
string& operator=(const string& s) {
	this->_str = new char[s.size() + 1];
	this->_size = s._size;
	this->_capacity = s._capacity;
	strcpy(this->_str, s._str);
	return *this;
}

注意:由于string类涉及内存的开辟,所以其拷贝构造和赋值重载必须手动写,不能使用系统默认生成的,否则只能完成浅拷贝,无法完成深拷贝。

4.析构函数

~string() {
	delete[]_str;
	_size = 0;
	_capacity = 0;
}

同理,析构函数也必须手动书写,否则无法完成空间释放工作。

5.测试

我们根据写的三种构造函数,可以使用四个对象来对其及逆行测试。        

void test1() {
	mystring::string s1("abcd");
	mystring::string s2(s1);
	mystring::string s3=s2;
	mystring::string s4="abcd";

	cout << "s1:" << s1 << endl;
	cout << "s2:" << s2 << endl;
	cout << "s3:" << s3 << endl;
	cout << "s4:" << s4 << endl;
}

测试通过

 四、迭代器函数

1.函数实现

在string类中,我们可以使用字符指针来模拟迭代器,其实现如下:

typedef char* iterator;
string::iterator string::begin() {
	return _str;
}

string::iterator string::end() {
	return _str + _size;
}

const string::iterator string::begin()const {
	return _str;
}

const string::iterator string::end()const {
	return _str + _size;
}

 2.测试

void test2() {
	mystring::string s1 = "hello world!";
	mystring::string::iterator it1 = s1.begin();
	while (it1 != s1.end()) {
		cout << *it1 << " ";
		it1++;
	}
	cout << endl;

	for (auto e : s1) {
		cout << e << " ";
	}
	cout << endl;

	const mystring::string s2 = "this is a sentance!";
	mystring::string::iterator it2 = s2.begin();
	while (it2 != s2.end()) {
		cout << *it2 << " ";
		it2++;
	}
	cout << endl;

	for (auto e : s2) {
		cout << e << " ";
	}
	cout << endl;
}

通过!

五、容量大小操作函数

1.size函数

作用:获取字符串大小

实现:直接返回_size即可

size_t size()const {
	return _size;
}

2.capacity函数

作用:获取string的当前容量

实现:直接返回_capacity即可

size_t capacity() const {
	return this->_capacity;
}

3.reserve函数

作用:将容量扩至指定值n,若n小于等于当前容量,则不进行操作,否则进行扩容。

实现:将原字符串的内容拷贝新字符串中,释放原来的空间。

void reserve(size_t n) {
	if (n > _capacity) {
		char* tmp = new char[n + 1];
		strcpy(tmp, _str);
		delete[]_str;
		_str = tmp;
		_capacity = n;
	}
}

4.resize函数

作用:改变字符串的大小至n,并且用字符ch填充字符串后面的内容,若n大于_capacity,则需要对字符串进行扩容操作。

实现:如果n<=_size,则将字符串的第n个位置改为'\0'即可,若n>_size,则将后面填充为ch即可。

void resize(size_t n, const char ch='\0') {
	if (n <= _size) {
		_str[n] = '\0';
        _size=n;
	}
	else {
		reserve(n);
		for (int i = _size; i < n; i++) {
			_str[i] = ch;
		}
		_str[n] = '\0';
		_size = n;
	}
}

5.empty函数

作用:判断字符串是否为空

实现:判断_size是否为0即可

bool empty()const {
	return _size == 0;
}

6.测试

void test3() {
	mystring::string s1 = "abcdefg";
	cout << "s1: " << s1.size()<<" "<<s1.capacity() << endl;

	cout << "test capacity:";
	//n<_capacity
	s1.reserve(3);
	cout << s1.capacity() << endl;
	s1.reserve(20);
	cout << s1.capacity() << endl;

	cout << "test resize:";
	//n<_size
	s1.resize(3);
	cout << s1 << endl;
	//n>_size
	s1.resize(10, '#');
	cout << s1 << endl;

	cout << "test empty:";
	cout << s1.empty() << endl;
}

通过!

六、修改类函数

1.push_back函数

作用:在末尾加一个元素ch

实现:将_str[_size]位置改为ch,再将后面加上一个'\0'即可,但是要注意扩容问题,以及记得需要修改_size的值

void push_back(const char ch) {
	if (_size == _capacity) {
		reserve(_capacity == 0 ? 4 : 2 * _capacity);
	}
	_str[_size] = ch;
	_size++;
	_str[_size] = '\0';
}

2.append函数

作用:在末尾追加一个字符串

实现:在字符串s的字符依次追加到后面即可,注意扩容问题,以及记得需要修改_size的值

void append(const char* s) {
	size_t len = strlen(s);
	if (_size + len > _capacity) {
		reserve(_size + len + 1);
	}
	strcpy(_str + _size, s);
	_size += len;
}

3.+=操作符重载

作用:在末尾追加一个字符或者是字符串

实现:根据追加的内容类型,选择调用append或者是push_back即可

void operator+=(const char ch) {
	push_back(ch);
}

void operator+=(const char* s) {
	append(s);
}

4.clear函数

作用:摸出字符串中的内容,使字符串的大小为0

实现:将字符串的第一个位置改为'\0',再将_str该为0即可

void clear() {
	if (_size != 0) {
		_str[0] = '\0';
		_size = 0;
	}

}

5.swap函数

作用:交换两个字符串的内容,包括_str,_szie,_capacity

实现:对_str,_size,_capacity直接调用std中的swap函数即可

void swap(string& s) {
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}

6.c_str函数

作用:返回字符串中_str的首地址,作用类似于begin函数

实现:直接返回其地址即可

const char* c_str() {
	return _str;
}

7.insert函数(const char ch)

作用:在pos位置插入一个字符

实现:将pos位置之后的元素全部往后移动一个位置,然后将pos位置改为ch

void insert(size_t pos, char ch){
	assert(pos <= _size);

    // 扩容2倍
	if (_size == _capacity){
		reserve(_capacity == 0 ? 4 : 2 * _capacity);
	}

	size_t end = _size + 1;
	while (end > pos){
		_str[end] = _str[end - 1];
		--end;
	}

	_str[pos] = ch;
	++_size;
}

8.insert函数(const char*s)

作用:在pos位置插入一个字符串

实现:将pos位置之后的元素全部往后移动strlen(s)个位置,然后将pos位置改为ch

void insert(size_t pos, const char* str){
	assert(pos <= _size);
	size_t len = strlen(str);
	if (_size + len > _capacity){
		// 扩容
		reserve(_size + len);
	}

	size_t end = _size + len;
	while (end > pos + len - 1){
		_str[end] = _str[end - len];
		end--;
	}

	strncpy(_str + pos, str, len);
	_size += len;
}

9.erase函数

作用:删除从pos位置开始的len个元素,如果pos+len>_size,则删除pos位置之后的所有元素

实现:如果pos+len>_size,则直接将pos位置改为'\0',再修改_size的值,否则直接将pos+len后面位置的元素直接拷贝到pos位置。再修改_size的值

void erase(size_t pos, size_t len = std::string::npos) {
	assert(pos < _size);
	if (len == std::string::npos || len >= _size - pos) {
		_str[pos] = '\0';
		_size = pos;
	}
	else {
		strcpy(_str + pos, _str + pos + len);
		_size -= len;
	}
}

10.测试

void test4() {
	mystring::string s1 = "abcd";
	//测试push_back
	s1.push_back('x');
	cout << s1 << endl;

	//测试qppend
	s1.append("hello worldz!");
	cout << s1 << endl;

	//测试+=
	s1 += '1';
	cout << s1 << endl;
	s1 += "234567";
	cout << s1 << endl;

	//测试clear
	s1.clear();
	cout << s1 << " s1._capacity:" << s1.capacity() <<"s1._size: "<< s1.size() << endl;

	//测试swap函数
	cout << "测试swap" << endl;
	mystring::string s2 = "hello world 12345";
	cout << s2 << endl;
	s1.swap(s2);
	cout << "s1: " << s1 << "s2: " << s2 << endl;


	cout << "测试C_str" << endl;
	//测试c_str
	cout << s1.c_str() << endl;

	//测试insert
	cout <<endl<< "测试insert" << endl;
	s1.insert(3, "haha");
	cout << s1 << endl;
	s1.insert(2, 's');
	cout << s1 << endl;

	//测试erase
	cout << "测试erase:" << endl;
	s1.erase(3, 5);
	cout << s1 << endl;
}

七、操作符重载

1.[ ]操作符

作用:取pos位置的内容返回

实现:直接返回pos位置的元素

char& operator[](int pos) {
	assert(pos < _size);
	return _str[pos];
}

const char& operator[](int pos)const {
	assert(pos < _size);
	return _str[pos];
}

2.<   <=   >   >=   ==   !=

作用:用于比较两个字符串

实现:利用mencpy函数来进行比较

bool operator<(const string& s) {
	memcmp(this->_str, s._str,max(this->_size,s._size)) < 0;
}

bool operator<=(const string& s) {
	memcmp(this->_str, s._str, max(this->_size, s._size)) <= 0;
}

bool operator>(const string& s) {
	memcmp(this->_str, s._str, max(this->_size, s._size)) > 0;
}

bool operator>=(const string& s) {
	memcmp(this->_str, s._str, max(this->_size, s._size)) >= 0;
}

bool operator==(const string& s) {
	if (this->_size != s._size)
		return false;

	return 
		memcmp(this->_str, s._str, max(this->_size, s._size)) == 0;
}

bool operator!=(const string& s) {
	return !((*this) == s);
}

3.测试 

void test5() {
	mystring::string s1 = "hello world!";
	for (int i = 0; i < s1.size(); i++) {
		s1[i]++;
		cout << s1[i] << " ";
	}
	cout << endl;
}

 

测试通过 

八、流插入和流提取重载

注意:流插入和流提取重载不能放在类内,必须在类外进行重载

1.流插入<<

作用:将字符串打印打印出来

实现:循环,遇到'\0'退出,并且返回流对象

ostream& operator<<(ostream& _cout, const mystring::string& s) {

	for (size_t i = 0; i < s.size(); ++i){
		if (s[i] == '\0')
			break;
		_cout << s[i];
	}
	return _cout;

}

2.流提取>>

作用:从流中读取数据到字符串中

实现:注意一点,为了避免频繁地扩容,可以将提取的字符暂存在一个数组中,等数组满了或者是读取结束后再转移到字符串中。

istream& operator>>(istream& _cin, mystring::string& s) {
		
	char ch;
	ch = _cin.get();
	char buff[128];
	size_t i = 0;
	while (ch != ' ' && ch != '\n') {
		buff[i++] = ch;
		if (i == 127) {
			buff[127] = '\0';
			s += buff;
			i = 0;
		}
		ch = _cin.get();
	}
	if (i > 0) {
		buff[i] = '\0';
		s += buff;
	}

	return _cin;
}

九、其他

find函数

作用:从pos位置检查字符ch或字符串s是否存在,存在则返回其第一次出现的位置

实现:直接进行比对,找到返回即可

size_t find(char c, size_t pos = 0) const{
	for (size_t i = pos; i < _size; ++i){
		if (_str[i] == c)
			return i;
	}
	return -1;//未找到
}
size_t find(const char* s, size_t pos = 0) const{
	assert(s);
	assert(pos < _size);
	const char* src = _str + pos;

	while (*src){
		const char* match = s;//如果不匹配,返回子串起始处重新查找
		const char* cur = src;
		while (*match && *match == *cur){
			++match;
			++cur;
		}
		if (*match == '\0'){
			return src - _str;//返回下标
		}
		else{
			++src;
		}
	}
	return -1;
}
void test6() {
	mystring::string s1 = "hello world!";
	cout<<s1.find('5')<<endl;
	cout << s1.find('l')<<endl;
	cout << s1.find("hello") << endl;
}

通过!

  • 25
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值