【C++之string类的模拟实现】

C++知识string类的模拟实现

前言:
前面篇章学习了C++对于string类的基本使用以及常用接口的认识,接下来继续学习,C++的string类的模拟实现等知识。
/知识点汇总/

1、模拟实现string

 class string
    {
        friend ostream& operator<<(ostream& _cout, const bit::string& s);
        friend istream& operator>>(istream& _cin, bit::string& s);
    public:
        typedef char* iterator;
        typedef const char* const_iterator;
        static const int npos;
    public:
        string(const char* str = "");
        string(const string& s);
        string& operator=(const string& s);
        ~string();
        //
        // iterator
        iterator begin();
        iterator end();
        const_iterator begin() const;
        const_iterator end() const;
        /
        // modify
        void push_back(char c);
        string& operator+=(char c);
        void append(const char* str);
        string& operator+=(const char* str);
        void clear();
        swap(string& s);
        const char* c_str() const;
        //substr返回子串
        string substr(size_t pos = 0, size_t len = npos);
        /
        // capacity
        size_t size() const;
        size_t capacity() const;
        bool empty() const;
        void resize(size_t n, char c = '\0');
        void reserve(size_t n);
        /
        // access
        char& operator[](size_t index);
        const char& operator[](size_t index) const;
        /
        // 返回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,并返回该字符的位置
        void insert(size_t pos, char c);
        void insert(size_t pos, const char* str);
        // 删除pos位置上的元素,并返回该元素的下一个位置
        void erase(size_t pos, size_t len);
    private:
        char* _str = nullptr;
        size_t _capacity = 0;
        size_t _size = 0;
    };
    const int string::npos = -1;

1.1、构造函数的两种方式

写法1:分离写法

//写法1:分离写法
string()
	//:_str(nullptr)
	//注意,传空串的处理,需要开一个字节的空间
	:_str(new char[1])
	, _size(0)
	, _capacity(0)
{
	_str[0] = '\0';//这样才好放入'\0'
}
string(const char* str)
	//:_str(str)//error,str为const,不允许修改
	:_size(strlen(str))//顺序的变换,防止随机值
	, _str(new char[strlen(str) + 1])
	, _capacity(strlen(str))
{}
//存在的问题:反复调用strlen使得效率低下。
//其次,当传空指针时,strlen不能接收空指针
//再三,初始化列表与成员变量的顺序一致,否则容易产生随机值。
//以上细节错误,需要多加注意。

写法2:合并写法,更推荐这样使用

//采用初始化列表分离,并只用一次strlen
		//string(const char* str = '\0')//error,字符对于string1无法隐式类型转换
		//string(const char* str = "\0")//可以,但不推荐,因为这种写法会有两个字符'\0'
		//string(const char* str = "")//空串,这样就行了,一个'\0'
		//string(const char* str = nullptr)//不能处理空串的情况
string(const char* str = "")
	:_size(strlen(str))
{
	_capacity = _size;
	_str = new char[_capacity + 1];
	strcpy(_str, str);
}

释放空间

//不管上述哪个方法对需要对应执行空间的释放
//析构函数
~string()
{
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}

1.2、运算符重载[]和函数重载

//返回size,写成const,保护内部数据不被修改,且外部普通对象可访问调用,const对象也可访问
		size_t size() const
		{
			return _size;
		}
		//返回容量
		size_t capacity() const
		{
			return _capacity;
		}
		//运算符重载[]
		//利用引用返回,返回的是别名,所以就实现了可读可写的操作
		//另外,此时注意,对于const对象就无法调用下标+[]了
		//char& operator[](size_t pos)
		//添加const,权限可以缩小,但不能放大
		//但是参考库里的写法,可以发现,提供的两个函数构成的函数重载。
		// 即: 各走各的函数。
		// 1.char& operator[] (size_t pos);
		char& operator[](size_t pos)
		{
			//检查是否越界
			assert(pos < _size);
			return _str[pos];
		}
		// 2.const char& operator[] (size_t pos) const; 
		const char& operator[](size_t pos) const
		{
			//检查是否越界
			assert(pos < _size);
			return _str[pos];
		}
		//其根本原因:
		//只管非const那么const对象就无法调用,只管const对象,
		//但又导致非const对象可访问但不能修改了,所以提供了函数重载.

1.3、iterator迭代器遍历以及c_str()转回字符数组

//迭代器版本1:const和非const
		typedef char* iterator;
		typedef const char* const_iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		const_iterator begin() const
		{
			return _str;
		}
		const_iterator end() const
		{
			return _str + _size;
		}
		//string转回字符数组
		const char* c_str()
		{
			return _str;
		}

1.4、插入insert/追加append/删除erase/尾插push_back/扩容reserve

//扩容
		void reserve(size_t n)
		{
			//判断这里,目的是兼容reserve的单独预先开辟空间的使用
			if (n > _capacity)
			{
				//char* tmp = new char[n];
				//值得注意的地方是,_capacity统计的是有效字符所用的空间大小,并不包括'\0'
				//所以,在开辟空间是的细节需要,额外加一个单位的空间给'\0'。
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}
		//尾插
		void push_back(char ch)
		{
			//检查容量--扩容(通常2倍,这里就不用checkcapacity了,最好先写reserve())
			if (_size == _capacity)
			{
				//reserve(2 * _capacity);//如果_capacity初始化为0,这样写就存在问题
				reserve(_capacity == 0 ? 4 : 2 * _capacity);//灵活控制
			}
			_str[_size] = ch;
			_size++;
			//注意把'\0'放在最后
			_str[_size] = '\0';
		}
		//追加
		void append(const char* str)
		{
			//检查容量--扩容(2倍不一定够了,最好的方式根据长度计算更好)
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);//灵活控制
			}
			//strcat//追加导'\0'到末尾,可以但效率不高
			strcpy(_str + _size, str);
			_size += len;
		}
		//运算符重载+=,同理函数重载
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}
		//插入,同理函数重载
		//插入操作涉及到一些检查操作
		void insert(size_t pos, char ch)
		{
			assert(pos <= _size);//pos==size是尾插,所以可加=
			//扩容
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
			}
			//真正的问题是pos == 0的处理
			//解决1:强转pos == end == -1结束 ==> 强转size_t的pos 1 --> -1
			//int end = _size;
			while (end >= (int)pos)
			//while (end >= (int)pos)
			//{
			//	_str[end + 1] = _str[end];
			//	--end;
			//}
			//解决2:巧妙的利用end=0就结束了,就不用解决强转问题
			size_t end = _size + 1;
			while (end > pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}
			_str[pos] = ch;
			++_size;
		}
		void insert(size_t pos, const char* str)
		{
			assert(pos <= _size);//pos==size是尾插,所以可加=
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);//灵活控制
			}
			size_t end = _size + len;
			while (end > pos)
			{
				_str[end] = _str[end - len];
				--end;
			}
			while (*str != '\0')
			{
				_str[pos++] = *str++;
				++_size;
			}
		}
		//删除,同理函数重载
		void erase(size_t pos, size_t len = npos)
		{
			//断言检查
			assert(pos < _size);//删除,不能取到'\0'的位置所以这里不能取=
			//判断长度是否超出size,超出说明是当前pos位置之后都进行删除
			//if (len == npos || pos + len >= _size)
			//pos+len有溢出的风险
			if (len == npos || len >= _size - pos)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
		}

1.5、swap/resize/find/substr/拷贝2构造,深浅拷贝

//resize改变的是有效元素个数,当调整之后的size大于容量时,容量会发生变化。 
		void resize(size_t n, char ch = '\0')//默认插入'\0'
		{
			if (n <= _size)
			{
				_str[n] = '\0';//截断即可
				_size = n;
			}
			else
			{
				reserve(n);
				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;
				}
				_str[n] = '\0';
				_size = n;
			}
		}
		//运算符重载= s1 = s3
		string& operator=(const string& s)
		{
			char* tmp = new char[s._capacity + 1];
			strcpy(tmp, s._str);
			delete[] _str;
			_str = tmp;
			_size = s._size;
			_capacity = s._capacity;
			return *this;
		}
		//swap同理,函数重载
		void swap(string& s)
		{
			//直接写成员函数交换,注意此处为了避免,编译器在此类域或命名空间找浪费时间或编译出错
			//直接使用域操作符,指定到std里找
			//其次复习:编译器默认查找匹配机制
			//先局部,再全局,即先现成再挪用
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
		//find,同理函数重载
		size_t find(char ch, size_t pos = 0) const //缺省参数只能从右向左赋初始值
		{
			assert(pos < _size);
			for (size_t i = pos; i < _size; i++)
			{
				if (_str[i] == ch)
					return i;
			}
			return npos;
		}
		size_t find(const char* str, size_t pos = 0) const//缺省参数只能从右向左赋初始值
		{
			assert(pos < _size);
			//1.kmp 2.bm算法
			//3.strstr
			//const char * strstr ( const char * str1, const char * str2 );
			//char* strstr(char* str1, const char* str2);
			const char* ptr = strstr(_str, str);
			if (ptr)
			{
				//指针减指针,下标位置
				return ptr - _str;
			}
			else
			{
				return npos;
			}
		}
		//substr返回子串
		string substr(size_t pos = 0, size_t len = npos)
		{
			string sub;
			//当前位置之后都取出
			//if (len == npos || len >= _size - pos)
			if (len >= _size - pos)
			{
				for (size_t i = pos; i < _size; i++)
				{
					sub += _str[i];
				}
			}
			else
			{
				for (size_t i = pos; i < pos + len; i++)
				{
					sub += _str[i];
				}
			}
			return sub;
		}

1.6、clear、运算符重载>,<,>=,<=,==,!=,+,-

//清空数据
		void clear()
		{
			_size = 0;
			_str[_size] = '\0';
		}
		//全局:运算符重载==
	bool operator==(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret == 0;
	}
	//同理全局:运算符重载<
	bool operator<(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret < 0;
	}
	//同理全局:运算符重载<=
	bool operator<=(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret <= 0;
		//复用
		//return s1 < s2 || s1 == s2;
	}
	//同理全局:运算符重载>
	bool operator>(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret > 0;
		//复用
		//return !(s1 <= s2);
	}
	//同理全局:运算符重载>=
	bool operator>=(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret >= 0;
		//复用
		//return !(s1 < s2);
	}
	//同理全局:运算符重载!=
	bool operator!=(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return !(ret == 0);
		//复用
		//return !(s1 == s2);
	}

1.7、流插入,流提取,getline

//全局,流插入、流提取
	ostream& operator<<(ostream& out, const string& s)
	{
		for (auto ch : s)
		{
			out << ch;
		}
		return out;
	}
	istream& operator>>(istream& in, string& s)
	{
		清空数据
		//s.clear();
		//char ch;
		in >> ch;
		//ch = in.get();//获取单个字符
		while (ch != ' ' && ch != '\n')
		//while (ch != '\n')//等价getline,提取一行直到遇见'\n'
		//{
		//	s += ch;
		//	//in >> ch;
		//	ch = in.get();
		//}
		此时发现返回值,会存在数据上的冲突,所以还需要在最前面清空空间的数据
		//return in;

		//优化:解决频繁的开辟空间
		s.clear();
		char ch = in.get();
		char buf[128];
		size_t index = 0;
		while (ch != ' ' && ch != '\n')
		{
			buf[index++] = ch;
			if (index == 127)//为了防止频繁扩容
			{
				buf[127] = '\0';
				s += buf;//控制一次性加127个字符
				//简单的说,利用缓冲区的原理
				index = 0;
			}
			ch = in.get();
		}
		if (index != 0)//处理尾巴置'\0',不满127个字符
		{
			buf[index] = '\0';
			s += buf;
		}
		return in;
	}
	//getline
	istream& getline(istream& in, string& s)
	{
		//清空数据
		s.clear();
		char ch;
		ch = in.get();//获取单个字符
		while (ch != '\n')
		{
			s += ch;
			ch = in.get();
		}
		return in;
	}

1.8、运算符重载优化:现代写法

//运算符重载= s1 = s3 -- 传统写法
		string& operator=(const string& s)
		{
			char* tmp = new char[s._capacity + 1];
			strcpy(tmp, s._str);
			delete[] _str;
			_str = tmp;
			_size = s._size;
			_capacity = s._capacity;
			return *this;
		}
		//优化:现代写法
		string& operator=(string& s)
		{
			//这里传值传参也是一种拷贝,s充当的就是tmp
			//使用传值拷贝,相当于一次拷贝构造函数的调用。
			swap(s);//直接交换
			return *this;//返回左值,支持连续赋值
		}
//拷贝构造s1(s2); -- 传统写法
		string(const string& s)
		{
			_str = new char[s._capacity + 1];//重新new一个大小相同的空间,并拷贝数据
			strcpy(_str, s._str);
			_size = s._size;
			_capacity = s._capacity;
		}
		//优化:现代写法,但注意s参数通常要默认置空
		string(const string& s)
		{
			//思想是,利用tmp这个中间变量,去构造一个一样的对象,然后与s2交换指针。
			//其次,如果一些内置成员的构造中未初始化,且编译器也未处理,就可能涉及随机值的访问,所以通常要给一个初始值(缺省值)
			string tmp(s._str);
			swap(tmp);
		}
		//运算符重载= s1 = s3 -- 传统写法
		string& operator=(const string& s)
		{
			char* tmp = new char[s._capacity + 1];//深度拷贝,防止指向同一块空间
			strcpy(tmp, s._str);
			delete[] _str;
			_str = tmp;
			_size = s._size;
			_capacity = s._capacity;
			return *this;
		}
		//现代写法
		string& operator=(const string& s)
		{
			string ss(s);
			swap(ss);
			return *this;
		}
		//继续优化.本质是一种复用,减少资源的损耗和提升效率
		string& operator=(string ss)
		{
			swap(ss);
			return *this;
		}

2、string类的实现完整代码

//模拟实现string的各个接口
	class string
	{
	public:
		//迭代器版本1:const和非const
		typedef char* iterator;
		typedef const char* const_iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		const_iterator begin() const
		{
			return _str;
		}
		const_iterator end() const
		{
			return _str + _size;
		}
		//构造函数
		string(const char* str = "")
			:_size(strlen(str))
		{
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		//析构函数,释放空间
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}
		//拷贝构造s1(s2)
		//优化:现代写法
		string(const string& s)
		{
			//思想是,利用tmp这个中间变量,去构造一个一样的对象,然后与s2交换指针。
			//其次,如果一些内置成员的构造中未初始化,且编译器也未处理,就可能涉及随机值的访问,所以通常要给一个初始值(缺省值)
			string tmp(s._str);
			swap(tmp);
		}

		//返回size,写成const,保护内部数据不被修改,且外部普通对象可访问调用,const对象也可访问
		size_t size() const
		{
			return _size;
		}
		//返回容量
		size_t capacity() const
		{
			return _capacity;
		}
		//string转回字符数组
		const char* c_str() const
		{
			return _str;
		}
		//运算符重载[]
		// 1.char& operator[] (size_t pos);
		char& operator[](size_t pos)
		{
			//检查是否越界
			assert(pos < _size);
			return _str[pos];
		}
		// 2.const char& operator[] (size_t pos) const; 
		const char& operator[](size_t pos) const
		{
			//检查是否越界
			assert(pos < _size);
			return _str[pos];
		}
		//扩容
		void reserve(size_t n)
		{
			//判断这里,目的是兼容reserve的单独预先开辟空间的使用
			if (n > _capacity)
			{
				//细节额外加一个单位的空间给'\0'。
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}
		//尾插
		void push_back(char ch)
		{
			//检查容量--扩容
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2 * _capacity);//灵活控制
			}
			_str[_size] = ch;
			_size++;
			//注意把'\0'放在最后
			_str[_size] = '\0';
			//复用insert
			//insert(_size, ch);
		}
		//追加
		void append(const char* str)
		{
			//检查容量--扩容
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);//灵活控制
			}
			strcpy(_str + _size, str);
			_size += len;
			//复用insert
			//insert(_size, str);
		}
		//运算符重载+=,同理函数重载
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}
		//插入,同理函数重载
		//插入操作涉及到一些检查操作
		void insert(size_t pos, char ch)
		{
			assert(pos <= _size);//pos==size是尾插,所以可加=
			//扩容
			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;
		}
		void insert(size_t pos, const char* str)
		{
			assert(pos <= _size);//pos==size是尾插,所以可加=
			size_t len = strlen(str);//注意此时不能用sizeof,不计入'\0'
			//扩容
			size_t end = _size + len;
			if (end > _capacity)
			{
				reserve(end);//灵活控制
			}
			//注意,pos + len -1需要减1,因为此时pos算的是下标,否则会少插入一位
			while (end > pos + len - 1)
			{
				_str[end] = _str[end - len];
				--end;
			}
			//方法2:调用库函数,但会连同'\0'一同拷贝,所以应征了不能用sizeof,并且注意_size最后+len
			strncpy(_str + pos, str, len);
			_size += len;
		}
		//删除,同理函数重载
		void erase(size_t pos, size_t len = npos)
		{
			//断言检查
			assert(pos < _size);//删除,不能取到'\0'的位置所以这里不能取=
			//判断长度是否超出size,超出说明是当前pos位置之后都进行删除
			if (len == npos || len >= _size - pos)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
		}
		//resize改变的是有效元素个数,当调整之后的size大于容量时,容量会发生变化。 
		void resize(size_t n, char ch = '\0')//默认插入'\0'
		{
			if (n <= _size)
			{
				_str[n] = '\0';//截断即可
				_size = n;
			}
			else
			{
				reserve(n);
				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;
				}
				_str[n] = '\0';
				_size = n;
			}
		}
		//运算符重载=
		//现代写法
		string& operator=(string ss)
		{
			swap(ss);
			return *this;
		}
		//swap同理,函数重载
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
		//find,同理函数重载
		size_t find(char ch, size_t pos = 0) const //缺省参数只能从右向左赋初始值
		{
			assert(pos < _size);
			for (size_t i = pos; i < _size; i++)
			{
				if (_str[i] == ch)
					return i;
			}
			return npos;
		}
		size_t find(const char* str, size_t pos = 0) const//缺省参数只能从右向左赋初始值
		{
			assert(pos < _size);
			//const char * strstr ( const char * str1, const char * str2 );
			const char* ptr = strstr(_str, str);
			if (ptr)
			{
				//指针减指针,下标位置
				return ptr - _str;
			}
			else
			{
				return npos;
			}
		}
		//substr返回子串
		string substr(size_t pos = 0, size_t len = npos)
		{
			string sub;
			//当前位置之后都取出
			if (len >= _size - pos)
			{
				for (size_t i = pos; i < _size; i++)
				{
					sub += _str[i];
				}
			}
			else
			{
				for (size_t i = pos; i < pos + len; i++)
				{
					sub += _str[i];
				}
			}
			return sub;
		}
		//清空数据
		void clear()
		{
			_size = 0;
			_str[_size] = '\0';
		}
	private:
		char* _str = nullptr;
		size_t _size = 0;
		size_t _capacity = 0;
	public:
		//静态成员变量,属于每个类属于所有对象
		static const int npos;
	};
	//静态成员变量类外定义
	const int string::npos = -1;
	//全局swap
	void swap(string& x, string& y)
	{
		x.swap(y);
	}
	//全局:运算符重载==
	bool operator==(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret == 0;
	}
	//同理全局:运算符重载<
	bool operator<(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret < 0;
	}
	//同理全局:运算符重载<=
	bool operator<=(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret <= 0;
		//复用
		//return s1 < s2 || s1 == s2;
	}
	//同理全局:运算符重载>
	bool operator>(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret > 0;
		//复用
		//return !(s1 <= s2);
	}
	//同理全局:运算符重载>=
	bool operator>=(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret >= 0;
		//复用
		//return !(s1 < s2);
	}
	//同理全局:运算符重载!=
	bool operator!=(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return !(ret == 0);
		//复用
		//return !(s1 == s2);
	}

	//全局,流插入、流提取
	ostream& operator<<(ostream& out, const string& s)
	{
		for (auto ch : s)
		{
			out << ch;
		}
		return out;
	}
	istream& operator>>(istream& in, string& s)
	{
		//优化:解决频繁的开辟空间
		s.clear();
		char ch = in.get();
		char buf[128];
		size_t index = 0;
		while (ch != ' ' && ch != '\n')
		{
			buf[index++] = ch;
			if (index == 127)//为了防止频繁扩容
			{
				buf[127] = '\0';
				s += buf;//控制一次性加127个字符
				//简单的说,利用缓冲区的原理
				index = 0;
			}
			ch = in.get();
		}
		if (index != 0)//处理尾巴置'\0',不满127个字符
		{
			buf[index] = '\0';
			s += buf;
		}
		return in;
	}
	//getline
	istream& getline(istream& in, string& s)
	{
		//清空数据
		s.clear();
		char ch;
		ch = in.get();//获取单个字符
		while (ch != '\n')
		{
			s += ch;
			ch = in.get();
		}
		return in;
	}

3、扩展知识:引用计数和写时拷贝

int main()
{
	std::string s1("111111111111111");
	std::string s2(s1);
	//不走深拷贝,采用引用计数和写时拷贝方法
	//1.析构多次
	//2.一个修改会影响另一个
	//3.计数

	//有了引用计数以后,析构时,先--引用计数;
	//如果计数被减完后,引用计数 == 0 代表当前是最后一个管理资源的对象,那么就释放。
	//这样就不同开辟其他相同的空间,巧妙的调整指向使用同一块空间。

	//其次,检查引用计数,如果等于1,说明资源是自己独占的,不用拷贝;
	//如果大于1,先深拷贝再写入,这就叫写时拷贝。

	//最后反正都要拷贝,搞这么复杂的意义在哪里?
	//答:不是每个对象都需要修改,当拷贝后不发生修改时就成功节省了资源。
	return 0;
}

引用计数和写时拷贝小结:

在C++中,引用计数(Reference Counting)和写时拷贝(Copy-on-Write,简称COW)是两种常用于实现共享和延迟复制的技术。
它们经常一起使用,以优化资源的使用和提高性能。
引用计数:
引用计数是一种内存管理技术,用于跟踪一个对象当前有多少个引用指向它。
每当一个新的引用指向一个对象时,引用计数增加;每当一个引用不再指向该对象时(例如,引用被销毁或重新赋值),引用计数减少。
当引用计数减少到0时,对象可以被安全地释放。
计数的一个主要优点是
它可以自动管理对象的生命周期,而无需显式的删除操作。
然而,它也有一些缺点:
比如循环引用问题(两个或多个对象相互引用,导致它们的引用计数永远不会降到0)和线程安全问题(需要额外的同步机制来确保引用计数的正确更新)。
写时拷贝:
写时拷贝是一种优化策略,用于延迟对象的复制操作。
其基本思想是:
在多个引用共享同一个对象时,并不立即创建对象的副本,而是等到某个引用尝试修改对象时才进行复制。
这样可以节省不必要的复制开销,特别是当对象很大或复制成本很高时。
写时拷贝通常与引用计数一起使用
当一个对象被多个引用共享时,它们实际上共享的是同一个对象的引用计数和内存地址。
当某个引用尝试修改对象时,系统会检查引用计数。
如果引用计数大于1,说明有其他引用也在使用这个对象,此时系统会创建一个新的对象副本,并更新相关引用的指向,以确保修改不会影响到其他引用。
如果引用计数为1,说明没有其他引用共享这个对象,那么可以直接在原对象上进行修改。
结合使用
引用计数和写时拷贝的结合使用可以带来显著的性能提升。
通过引用计数,我们可以高效地管理对象的生命周期和共享状态;通过写时拷贝,我们可以延迟不必要的复制操作,提高程序的响应速度和资源利用率。

  • 20
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值