C++---string类的使用和模拟实现

本文详细介绍了C++中深浅拷贝的概念,区分了浅拷贝(值拷贝)和深拷贝在指针类型变量中的应用。接着,重点讨论了C++标准库中的string类,包括其构造函数、析构函数、赋值运算符重载、迭代器以及各种成员函数的使用方法,如插入、查找、转换为C风格字符串等。此外,还探讨了如何实现自定义string类的关键接口,如拷贝构造、赋值运算符重载和扩容策略。最后,提供了一些测试用例以验证接口的正确性。
摘要由CSDN通过智能技术生成

 

目录

一、深浅拷贝

二、string接口使用

三、重要接口的实现和解释

四、完成代码 + 测试用例


一、深浅拷贝

1.浅拷贝

浅拷贝也称值拷贝。定义两个变量int a = 10;int b = 15;  b = a就是一种浅拷贝,即将a的值给b。

2.深拷贝

对于指针类型的变量,使用浅拷贝:int* a = 10; int* b = 20; b = a这是a和b就会指向同一块内存空间,因为采用浅拷贝会将a的值拷贝给b,而a的值是int型变量的地址。

深拷贝:int* a = 10;int* b = 20; *b = *a就是一种深拷贝,他不是直接将变量的值拷贝给另一个变量,他是将这块地址中的内容拷贝给另一个变量,这时a,b指向的不是同一块空间,但是a,b的值相等。

3.深浅拷贝的使用

对于像日期类那样的类,它的成员变量都是int类型,可以直接用浅拷贝。而接下来要学的string类中,存在char*这样的类型,在使用默认构造或者拷贝构造创建一个对象时如果使用浅拷贝就会导致两个对象指向同一块内存,程序结束后会对这一块内存释放两次,导致程序崩溃。

二、string接口使用

1.构造函数

构造函数主要用来初始化类成员变量,在使用类实例化对象时,编译器会自动调用其,默认构造函数。

默认构造:string(const char* str = "")  

       使用:string s1;string s2 = "hello world";

拷贝构造:string(const string& s)

       使用:string s1 = "hello world";string s2 = s1;

2.析构函数

~string();//完成资源的释放,由编译器自己调用

3.赋值运算符重载

string& operator=(const string& s); 

两种使用方式:s1 = s2;s1 = s2 = s3(支持连续赋值);

4.[]重载

char& operator[](size_t pos);//返回pos位置的字符

使用:cout<<s1[0]<<endl;

5.swap

void swap(string& s) //交换两个string对象

使用:s1.swap(s2); 

6.reserve和resize

void reserve(size_t n);//将容量扩大到n,如果n小于capacity则大小不变,如果n大于capacity则增加到n

void resize(size_t n,char ch = '\0');//改变字符串的size

        如果n<size,则丢掉size位置及其之后的字符

       如果n>size,则在size之后位置补n-size个ch

8.size和capacity

size_t size();//字符串长度
size_t capacity();//容量

9.iterator

 iterator begin();//字符串的开始
 iterator end();//字符串的结束

使用:string s1("hello world"); string::iterator it = s.begin();

主要用于字符串的遍历,在使用while循环时使用iterator比较方便(iterator也适用auto自动推到类型)

10.字符串的插入

void insert(size_t pos,char ch);//在pos位置插入一个字符ch

void push_back();//在字符串的末尾插入一个字符

void insert(size_t,char* str);//在pos位置插入一个字符串

void append(const char* str);//在字符串末尾追加一个字符串str

void operator+=(const char ch);//和push_back功能相同,在字符串结尾插入一个字符

void operator+=(const char* str);//在字符串末尾插入一个字符串

11.字符串查找

size_t find(const char ch,size_t pos = 0);//从pos个位置开始查找字符ch第一次出现的位置,如果没有返回npos

size_t find(const char* str,size_t pos = 0);//从pos个位置开始找字符串str,如果没有返回npos

12.将字符串转换成C语言形式的字符串

char* c_str();

13.输入、输出重载

ostream& operator<<(ostream& _cout,const sring& s);//使用cout<<输出字符串

istream& operator>>(istream& _cin,const string& s);//使用cin>>输入字符串

14.比较大小

bool operator>(const string& s1,const string& s2);//比较两个字符串的大小

         //s1="helloworld" s2="hello"   --->s1 > s2

         //s1="hello" s2="hello"--->s1==s2

        //s1="hello" s2="aellobbb" --->s1>s2

bool operator<(const string& s1,const string& s2);

bool operator>=(const string& s1,const string& s2);

bool operator<=(const string& s1,const string& s2);

bool operator==(const string& s1,const string& s2);

bool operator!=(const string& s1,const string& s2);

15.其他比较重要的接口

clear();//清空字符串

empty()//判断字符串是否为空串

getline();//获取一行字符

 

三、重要接口的实现和解释

在面试过程中,如果面试官要求实现一个string类,一般情况下实现其默认构造、拷贝构造、赋值运算符重载、析构即可。

1.拷贝构造的多种实现方法

1)传统写法

myString::string::string(const string& s)
{
	_str = new char[strlen(s._str) + 1];
	strcpy(_str,s._str);
	_size = _capacity = strlen(s._str);
}

2)现代写法

使用s构造出一个新的对象tmp,将this对象和tmp对象进行交换(_str、_size、_capacity都要交换)。注意,tmp是一个局部对象,函数结束后会自动调用其析构函数对原空间进行释放。

myString::string::string(const string& s)
:_str(nullptr)
, _size(0)
, _capacity(0)
{
	string tmp(s._str);
	swap(tmp);
}

2.赋值运算符重载两种方法比较

1)传统写法

myString::string& myString::string::operator=(const string& s)
{
	if (this != &s)
	{
		//开辟新空间
		char* tmp = new char[strlen(s._str) + 1];
		strcpy(tmp, s._str);
		_size = _capacity = s._size;
		//释放原空间
		delete[] _str;
		_str = tmp;
	}
	return *this;
}

2)现代写法

由于参数没有使用引用,因此在调用该赋值运算符重载函数时会先调用拷贝构造函数拷贝出一个临时对象s,然后在调用该操作运算符重载函数,将临时对象和this对象进行深度拷贝。函数调用结束后,s会调用其析构函数对原内存进行释放。

myString::string& myString::string::operator=(myString::string s)
{
	swap(s);
	return *this;
}

3.扩容

在一个字符串中插入一个字符串时,如果原字符串空间不足,则需要对原字符串进行扩容。扩容时,为了提高效率往往会扩容为原来的两倍。在字符串中,每一个字符串都是以'\0'结尾的,因此在扩容时,如果我们需要将容量扩大到n,则 需要申请n+1个空间,其中一个用来存储结束标志符'\0'。

void myString::string::reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n+1];//多申请一个用来保存结束标志符
		strcpy(tmp,_str);
		delete[] _str;
		_str = tmp;
		_capacity = n;
	}
}

4.iterator

在string类中,iterator的底层实现实际上就是指针。

//typedef char* iterator;
myString::string::iterator myString::string::begin()
{
	return _str;
}
myString::string::iterator myString::string::end()
{
	return _str + _size;
}

四、完成代码 + 测试用例

string.h

#include<iostream>
#include<assert.h>
using namespace std;


namespace myString
{
	class string
	{
	public:
		//iterator
		typedef char* iterator;
		iterator begin();
		iterator end();
		//默认构造
		string(const char* str = "");
		//拷贝构造
		string(const string& s);
		void swap(string& s1);
		//赋值运算符重载
		//string& operator=(const string& s);
		string& operator=(myString::string s);
		//析构函数
		~string();

		//[]重载
		char& operator[](size_t pos);
		//
		void reserve(size_t n);
		void resize(size_t n,char ch = '\0');
		size_t size();
		size_t capacity();
		//插入
		void push_back(const char ch);
		void insert(size_t pos,const char ch);
		void insert(size_t pos,const char* str);
		void append(const char* str);
		void operator+=(const char ch);
		void operator+=(const char* str);
		//查找
		size_t find(char ch, size_t pos = 0);
		size_t find(char* str, size_t pos = 0);
		//转换成c字符串
		const char* c_str() const;
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	public:
		const static size_t npos;
	};
	ostream& operator<<(ostream& _cout,myString::string s);
	istream& operator>>(istream& _cin, myString::string s);
	bool operator>(myString::string& s1, myString::string& s2);
	bool operator==(myString::string& s1, myString::string& s2);
	bool operator>=(myString::string& s1, myString::string& s2);
	bool operator<=(myString::string& s1, myString::string& s2);
	bool operator!=(myString::string& s1, myString::string& s2);
};

string.cpp

#include"string.h"

const size_t myString::string::npos = -1;

//默认构造函数
myString::string::string(const char* str)
{
	//不能使用nullptr床架对象
	assert(str != nullptr);
	_str = new char[strlen(str) + 1];
	_size = _capacity = strlen(str);
	strcpy(_str,str);
}
//拷贝构造函数-现代写法
//myString::string::string(const string& s)
//:_str(nullptr)
//, _size(0)
//, _capacity(0)
//{
//	string tmp(s._str);
//	swap(tmp);
//}

//拷贝构造-传统写法
myString::string::string(const string& s)
{
	_str = new char[strlen(s._str) + 1];
	strcpy(_str,s._str);
	_size = _capacity = strlen(s._str);
}

//析构函数
myString::string::~string()
{
	delete[] _str;
	_size = _capacity = 0;
	_str = nullptr;
}

//赋值运算符重载-传统写法
//myString::string& myString::string::operator=(const string& s)
//{
//	if (this != &s)
//	{
//		//开辟新空间
//		char* tmp = new char[strlen(s._str) + 1];
//		strcpy(tmp, s._str);
//		_size = _capacity = s._size;
//		//释放原空间
//		delete[] _str;
//		_str = tmp;
//	}
//	return *this;
//}

//赋值运算符重载-现代写法
myString::string& myString::string::operator=(myString::string s)
{
	swap(s);
	return *this;
}
//[]重载
char& myString::string::operator[](size_t pos)
{
	assert(pos < _size);
	return _str[pos];
}
//交换两个字符串对象
void myString::string::swap(string& s1)
{
	std::swap(_str, s1._str);
	std::swap(_size,s1._size);
	std::swap(_capacity,s1._capacity);
}

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

void myString::string::resize(size_t n, char ch)
{
	if (n < _size)
	{
		_str[_size] = '\0';
		_size = n;
	}
	else
	{
		if (n > _capacity)
		{
			reserve(n);
		}
		//后边补ch
		for (size_t i = _size; i < n; i++)
			_str[i] = ch;
		//加'\0'
		_str[n] = '\0';
		_size = n;
	}
}

size_t myString::string::size()
{
	return _size;
}
size_t myString::string::capacity()
{
	return _capacity;
}

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

void myString::string::insert(size_t pos,const char ch)
{
	//检查插入位置是否正确
	assert(pos <= _size);
	//判断是否需要扩容
	if (_capacity == 0)
		reserve(10);
	else if (_capacity == _size)
		reserve(2*_capacity);
	//将size及其之后的字符后移
	for (size_t i = _size; i >= pos; i--)
	{
		_str[i+1] = _str[i];
	}
	//pos位置插入字符
	_str[pos] = ch;
	_size++;
}

void myString::string::insert(size_t pos, const char* str)
{
	int len = strlen(str);
	//检查插入位置是否正确
	assert(pos <= _size);
	//判断是否需要扩容
	if (_capacity == 0)
		reserve(len);
	else if (_capacity == _size)
		reserve(_size+len);
	//移动size之后的字符
	for (size_t i = _size; i >= pos; i--)
	{
		_str[i+len] = _str[i];
	}
	//插入字符串
	strncpy(_str+pos,str,len);
	_size += len;
}

void myString::string::push_back(const char ch)
{
	insert(_size,ch);
}

void myString::string::append(const char* str)
{
	insert(_size,str);
}

void myString::string::operator+=(const char ch)
{
	push_back(ch);
}
void myString::string::operator+=(const char* str)
{
	append(str);
}

size_t myString::string::find(char ch, size_t pos)
{
	assert(pos < _size);
	for (size_t i = 0; i < _size; i++)
	{
		if (_str[i] == ch)
			return i;
	}
	return npos;
}
size_t myString::string::find(char* str, size_t pos)
{
	const char* ret = strstr(_str + pos, str);
	if (ret == nullptr)
	{
		return npos;
	}
	else
	{
		return ret - _str;
	}
}

const char* myString::string::c_str() const
{
	return _str;
}

ostream& myString::operator<<(ostream& _cout,myString::string s)
{
	for (size_t i = 0; i < s.size(); i++)
	{
		_cout << s[i];
	}
	_cout << endl;
	return _cout;
}
istream& myString::operator>>(istream& _cin, myString::string s)
{
	s.resize(0);
	char ch;
	while (1)
	{
		//in>>ch;
		_cin.get(ch);
		if (ch == ' ' || ch == '\n')
		{
			break;
		}
		else
		{
			s += ch;
		}
	}

	return _cin;
}

bool myString::operator>(myString::string& s1, myString::string& s2)
{
	size_t i1 = 0, i2 = 0;
	while (i1 < s1.size() && i2 < s2.size())
	{
		if (s1[i1] > s2[i2])
		{
			return true;
		}
		else if (s1[i1] < s2[i2])
		{
			return false;
		}
		else
		{
			++i1;
			++i2;
		}
	}

	if (i1 < s1.size())
	{
		return true;
	}
	else if (i2 < s2.size())
	{
		return false;
	}
	else
	{
		return false;
	}
}

bool myString::operator==(myString::string& s1, myString::string& s2)
{
	size_t i1 = 0, i2 = 0;
	while (i1 < s1.size() && i2 < s2.size())
	{
		if (s1[i1] > s2[i2])
		{
			return false;
		}
		else if (s1[i1] < s2[i2])
		{
			return false;
		}
		else
		{
			++i1;
			++i2;
		}
	}

	if (i1 == s1.size() && i2 == s2.size())
	{
		return true;
	}
	else
	{
		return false;
	}
}

bool myString::operator>=(myString::string& s1, myString::string& s2)
{
	if (s1 > s2 || s1 == s2)
		return true;
	return false;
}

bool myString::operator<=(myString::string& s1, myString::string& s2)
{
	if (s1 > s2)
		return false;
	return true;
}

bool myString::operator!=(myString::string& s1, myString::string& s2)
{
	if (s1 == s2)
		return false;
	return true;
}

test.cpp

#include"string.h"

int main()
{
	//测试默认构造函数
	myString::string s1;
	//myString::string s2 = NULL;
	myString::string s3 = "hello world";

	//测试拷贝构造
	myString::string s4(s3);

	//赋值运算符重载
	myString::string s5("hello");
	s5 = s4;
	//[]重载
	cout << s5[6] << endl;
	//reserve和resize
	//reserve在插入的时候会调用
	myString::string s6;
	s6.resize(5,'x');
	//size和capacity
	cout << s6.size() << " " << s6.capacity() << endl;//均为5

	//iterator
	myString::string::iterator it = s6.begin();
	while (it < s6.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;

	//插入
	myString::string s7("helloworld");
	s7.insert(5,' ');
	s7.push_back('!');
	myString::string s8("12345678");
	s8.insert(2,"hello");
	s8.append("world!");
	//输出
	cout << s8;

	cout << (s7 == s8) << endl;
	return 0;
}

 

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

疯狂嘚程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值