模拟实现string&&其中的一些知识点

本文深入探讨了C++中的深拷贝与浅拷贝概念,解释了两者之间的区别及其在字符串类String实现中的应用。通过具体代码示例,详细分析了拷贝构造函数和赋值运算符如何正确实现深拷贝,以避免资源重复释放导致的程序崩溃。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

知识点列举

  1. 使用初始化列表的好处
  2. 拷贝构造的形参为&与值的区别
  3. 深拷贝与浅拷贝
  4. 使用&的好处
  5. 形参使用const引用的好处

使用初始化列表的好处

  1. 类成员中存在常量,如const int a,只能用初始化不能复制
  2. 类成员中存在引用,同样只能使用初始化不能赋值。类成员中存在引用,同样只能使用初始化不能赋值。
  3. 提高效率:对类类型来说,用初始化列表少了一次调用默认构造函数的过程,
//模拟实现string

class String
{

public:
	//构造函数
	String(const char* str =“”)_size(strlen(str))
	, _str(new char[_size + 1])
	,_capacity( _size)
	{
		strcpy(_str, str);
	}
	
	String(char* str, size_t size)_size(size)
	,_capacity(size)
	,_str(_str = new char[size + 1])
	{
		strncpy(_str, str, size);
	}

拷贝构造的形参为&与值的区别

  如果拷贝构造函数的形参用值,由于类中没有这个对象,就要调用拷贝构造函数来创建,以此类推,会造成无限递归循环

	//拷贝构造函数
	String(const String& s)
		:_str(new char[s._capacity + 1])
		, _capacity(s._capacity)
		, _size(s._size)
	{
		strcpy(_str, s._str);
	}

深拷贝与浅拷贝

  1. 浅拷贝:也称位拷贝,编译器只是直接将指针的值全部拷贝过来,从而造成对个对象使用同一块空间,当一个对象将这块内存释放掉之后,另一些对象不知道该块空间已经还给了系统,以为还有效,依旧会对这块空间进行释放,在对这段内存进行释放操作的时候,一个内存空间被释放多次,发生了访问违规,导致程序崩溃。
  2. 深拷贝:为了解决浅拷贝的问题,深拷贝则不是直接将指针的值拷贝,是为指针_str开辟与str相同大小的内存空间,然后将str中的资源拷贝到_str中,这样虽然_str与str中的资源一样,但是使用的是两块独立的空间,所以delete时只释放这个对象自己的内存,不会出现访问违规。
	//赋值运算符
	String operator=(const String& s)
	{
		if (this != &s)
		{
			char* pStr = new char[s._capacity + 1];
			strcpy(pStr, _str);
			delete[] _str;
			_str = pStr;
			_capacity = s._capacity;
			_size = s._size;
		}

		return *this;
	}

形参使用&的好处

  形参是对象的引用,是通过传地址的方法传递参数的,对函数形参的改变就是对实参的改变,如果函数的形参是对象,则是通过传值的方法传递参数的,函数体内对形参的修改无法传到函数体外。

运算符重载部分

	//运算符重载部分
	bool operator<(const String& s)
	{

		return !strcmp(_str, s._str);
	}
	bool operator<=(const String& s)
	{
		if (strcmp(_str, s._str) == 1)
			return npos;
		return 1;
	}
	bool operator>(const String& s)
	
	{
		return strcmp(_str, s._str);
	}
	bool operator>=(const String& s)
	{
		if (strcmp(_str, s._str) != 1)
			return npos;
		return 1;
	}
	bool operator==(const String& s)
	{
		return (strcmp(_str, s._str) == 0);
	}
	bool operator!=(const String& s)
	{
		return (strcmp(_str, s._str) != 0);
	}
	friend ostream& operator<<(ostream& _cout, const String& s)
	{
		_cout << s._str;

		return _cout;
	}
	friend istream& operator>>(istream& _cin, String& s)
	{
		_cin >> s._str;

		return _cin;
	}

形参使用const的好处

  1. 当实参的类型比较大时,复制开销很大,引用会“避免复制”。
  2. “避免修改实参”,当使用引用时,如果调用者希望只使用实参并不修改实参,则const可以避免使用该引用修改实参。

string中部分接口的模拟实现

	//尾插
	void PushBack(char c)
	{
		//CheckCapacity();
		_str[_size++] = c;
		_str[_size] = '\0';
	}
	 //追加字符串
	void Append(const char* str)  
	{
		//判断对象中的剩余空间是否能够放下,放不下就开辟新空间
		int len = strlen(str);
		if (len > _capacity - _size)
			Reserve(2 * _capacity + len);
		strcat(_str, str);
	}
	char& operator[](size_t index)
	{
		return _str[index];
	}

	const char& operator[](size_t index)const				
	//at与[]唯一不同的方式是前者越界会抛出异常,后者越界会崩溃,或者给随机值
	{
		return _str[index];
	}
	
	void Reserve(size_t newCapacity)
	{
		if (_capacity <= newCapacity && _capacity != newCapacity)
		{
			char* pStr = new char[newCapacity + 1];
			strcpy(pStr, _str);
			delete[] _str;
			_str = pStr;
			_capacity = newCapacity;
		}
	}

	void ReSize(size_t newSize, char c)
	{
		if (newSize > _size)
		{
			
			/*自己实现memset函数
			size_t size = _size;
			for (size_t i = 0; i < newSize; ++i)
			{
				pStr[size++] = c;
			}
			delete[] _str;
			_str = pStr;*/
			Reserve(newSize);
			memset(_str + _size, c, newSize - _size);
			_str[newSize] = '\0';
			_capacity = newSize;
		}
		else if (newSize < _size)
		{
			/*char* pStr = new char[newSize + 1];
			for (size_t i = 0; i < newSize; ++i)
			{
				pStr[i] = _str[i];
			}
			delete[] _str;
			_str = pStr;*/
			
			memset(_str + newSize, '\0', 1);
		}
		_size = newSize;
		
		
	}
	
	int Size()const
	{
		return _size;
	}

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

	int Find(char c, size_t pos = 0)
	{
		for (size_t i = pos; i < _size; ++i)
		{
			if (c == _str[i])
				return i;
		}

		return npos;
	}

	int rFind(char c)
	{
		for (size_t i = _size - 1; i > 0; --i)
		{
			if (c == _str[i])
				return i;
		}

		return npos;
	}
	//返回c格式的字符串
	const char* C_str()const			//返回c格式的字符串
	{
		return _str;
	}

	void Swap(String& s)
	{
		swap(_str, s._str);
		swap(_size, s._size);
		swap(_capacity, s._capacity);
	}
	//从_str中返回从pos开始的size个字符的子字符串
	String StrSub(size_t pos, size_t size)   
	{
	 //统计pos之后的所有字符数量
		int len = strlen(_str + pos);  
		  
	//如果size超过了字符串实际长度,将size设置为pos之后的所有字符数量
		if (len < size)					
			size = len;
	//将_str中从pos位置开始的字符串传递size个回去
		return String(_str + pos, size);   
	}
	//析构函数
	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}


private:
	char* _str;
	size_t _capacity;    //容量
	size_t _size;		 //有效字符的个数

	const static int npos;
};

const int String::npos = -1;


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值