C++容器---string知识点总结

1.标准库中的string类

    简单概述:

        1.string是表示字符串的字符串类

        2.该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作

        3.string在底层实际是:basic_string模板类的别名,typedef basic_string<cahr, char_traits, allcoator> string

        4.注意:这个类独立于所使用的编码类处理字节:如果用来处理多字节或者变成字节(如UTF-8)的序列,这个类的所有成员以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作

        5.在使用string类时,必须包含头文件及using namespace std;

    string常用接口

        1.string类对象的常见构造

          

        2.string类对象的容量操作

          

          注意:

             1.size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()

             2.clear()只是将string中有效字符清空,不改变底层空间大小

             3.resize(size_t n)与resize(size_t n, char n)都是将字符串有效字符个数改变到n个,不同的是当字符个数增多时:前者用0来填充多出的元素空间,后者用字符c来填充多出的元素空间,注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减小,底层空间总大小不变

             4.reserve:为string预留空间,不改变有效元素个数当reserve的参数小于string的底层空间总大小时,reserve不会改变容量大小

    string类对象的访问操作

       

    string类对象的修改操作

       

      注意:

          1.在string尾部追加字符时,s.push_back(c)/s.append(1,c)/s += 'c'三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅仅可以连接单个字符,而且可以连接字符串

          2.对string操作时,如果能够大概预估放多少字符,可以先通过reserve把空间预留好

2.string类的模拟实现

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

class String{
public:
	typedef char* iterator;
	typedef const char* const_iterator;
	//构造函数
	String(char *str = ""){
		assert(str != nullptr);
		//_size:有效字符数量,不包含'\0'
		_size = strlen(str);
		_str = new char[_size + 1];
		strcpy(_str, str);//while(*str1++ = *str2++);
		_capacity = _size;
	}
	//拷贝构造
	String(const String& s) 
		:_str(nullptr)
		, _size(0)
		, _capacity(0)
	{
		String tmp(s._str);
		Swap(tmp);
	}

	//交换函数
	void Swap(String& s) {
		swap(_str, s._str);
		swap(_size, s._size);
		swap(_capacity, s._capacity);
	}

	//赋值运算符重载
	String& operator=(String s){
		Swap(s);
		return *this;
	}

	//[]形式访问字符,可读可改
	char& operator[](size_t pos) {
		assert(pos < _size);
		return _str[pos];
	}

	//[]只读,不可改
	const char& operator[](size_t pos) const {
		assert(pos < _size);
		return _str[pos];
	}

	//迭代器的使用
	//返回begin位置,可读可写
	iterator begin() {
		return _str;
	}

	//返回end的位置,可读可写
	iterator end() {
		return _str + _size;
	}

	//返回begin位置,只读
	const_iterator begin()const {
		return _str;
	}

	//返回end位置,只读
	const_iterator end()const {
		return _str + _size;
	}

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

	char* c_str(){
		return _str;
	}

	//在尾部插入
	void PushBack(char c) {
		//检查空间是否足够
		if (_size == _capacity){
			//初始化容量设置为15
			size_t newC = (_capacity == 0 ? 15 : 2 * _capacity);
			Reserve(newC);
		}
		_str[_size++] = c;
		_str[_size] = '\0';
	}

	//扩容
	void Reserve(size_t n) {
		if (n > _capacity){
			char *tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete _str;
			//让指针指向新空间
			_str = tmp;
			//更新容量
			_capacity = n;
		}
	}

	//尾删
	void PopBack(){
		if (_size > 0){
			_str[_size--] = '\0';
		}
	}

	//设置有效字符的大小
	void Resize(size_t n, char c = '\0') {
		if (n > _capacity){
			Reserve(n);
		}

		if (n > _size){
			memset(_str + _size, c, n - _size);
		}
		_size = n;
		_str[_size] = '\0';
	}

	//从指定位置删除元素,可以指定删除长度
	void Erase(size_t pos, size_t len) {
		assert(pos < _size);
		if (pos + len >= _size){
			_size = pos;
			_str[_size] = '\0';
		}
		else{
			//从pos+len位置开始挪动数据,每个数据向前挪动len位置
			for (int i = pos + len; i <= _size; i++){
				_str[pos++] = _str[i];
			}
			_size -= len;
		}
	}

	//追加
	void Append(const char *str) {
		size_t sz = strlen(str);
		if (_size + sz > _capacity){
			Reserve(_size + sz);
		}
		//从'\0'位置开始插入字符串str
		strcpy(_str + _size, str);
		_size += sz;
	}

	//插入一个字符
	void Insertc(size_t pos, char c) {
		//检查容量:是否增容
		//元素挪动
		//更新size,补'\0'
		assert(pos <= _size);
		if (_size == _capacity){
			size_t newCapacity = (_capacity == 0 ? 15 : _capacity * 2);
			Reserve(newCapacity);
		}

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

	//插入一个字符串
	void Insertstr(size_t pos, const char *str){
		//检查容量:是否增容
		//挪动元素
		//更新size,补'\0'
		assert(pos <= _size);
		size_t len = strlen(str);
		//不能使用给_capacity*2的方法,如果2倍之后还是还是小于_size+len就会出错
		if (_size + len > _capacity){
			Reserve(_size + len);
		}

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

		//插入字符串,不能使用strcpy(),strcpy(_str+pos, str)
		//会把str中的'\0'一起拷贝过去,导致有效内容被覆盖
		//strncpy可以,指定拷贝内容的大小strncpy(_str+pos, str, len)
		while (*str){
			_str[pos++] = *str++;
		}

		_size += len;
	}

	//查找一个字符
	size_t find(char c) {
		for (int i = 0; i < _size; i++){
			if (_str[i] == c){
				return i;
			}
		}
		return npos;
	}

	//查找一个字符串
	size_t find(const char *str, size_t pos = 0){
		const char *posPtr = subStrPos(_str + pos, str);
		if (posPtr){
			return posPtr - _str;
		}

		return npos;
	}

	String& operator+=(char c){
		PushBack(c);
		return *this;
	}

	String& operator+=(const char* str) {
		Append(str);
		return *this;
	}

	String& operator+=(const String& s) {
		*this += s._str;
		return *this;
	}
private:
	char *_str;
	size_t _size;
	size_t _capacity;
public:
	//静态成员在类外初始化
	static const size_t npos;
};

const size_t String::npos = -1;

String operator+(const String& s1, const String& s2){
	String tmp(s1);
	return tmp + s2;
}

String operator+(const String& s1, const char *str){
	String tmp(s1);
	return tmp + str;
}

String operator+(const String& s1, char c) {
	String tmp(s1);
	return tmp + c;
}

const char* subStrPos(const char *dest, const char *src) {
	const char *dt = dest;
	const char *sc = src;
	while (*dt){
		//如果第一个字符相等,匹配剩余字符
		if (*dt == *sc){
			const char *mathdest = dt + 1;
			const char *mathsrc = sc + 1;
			//匹配剩余字符
			while (*mathsrc && *mathdest){
				//如果当前带匹配字符不相等,停止匹配
				if (*mathdest != *mathsrc)
					break;
				//继续向后匹配
				mathdest++;
				mathsrc++;
			}

			if (*mathsrc == '\0'){
				//匹配成功,返回当前子串在目标字符串中的首元素出现的位置
				return dt;
			}
		}
		dt++;
	}

	return nullptr;
}

    浅拷贝:编译器只是将对象中的值拷贝过来。

        注意:如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道资源已经被释放,以为还有效,所以当继续对资源进行操作时,就会发生访问违规。

    深拷贝:如果一个类中涉及到资源管理,其拷贝构造函数、赋值运算符重载函数以及析构函数必须显示给出。

    写是拷贝:是在浅拷贝的基础上添加了引用计数的方式来实现的。

        引用计数:用来记录资源使用者的个数,在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,如果计数为1,说明该对象是资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值