C++ string类深浅拷贝问题详解

string类对象的容量操作

函数名称 功能说明
size 返回字符串有效字符长度
length 返回字符串有效字符长度
capacity 返回空间总大小
empty 检测字符串释放为空串,是返回true,否则返回false
clear 清空有效字符
reserve 为字符串预留空间
resize 将有效字符的个数该成n个,多出的空间用字符c填充

注意

  1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一 致,一般情况下基本都是用size()。
  2. clear()只是将string中有效字符清空,不改变底层空间大小。
  3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字 符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的 元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大 小,如果是将元素个数减少,底层空间总大小不变。
  4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于 string的底层空间总大小时,reserver不会改变容量大小。 除非newCapacity < 15。并且,如果改变的空间大小小于了有效元素个数还是不会改变空间大小。

string类的模拟实现

class String 
{ 
public:    
	String(const char* str = "")    
	{ 
		if(nullptr == str)
 		{
			assert(false);
			return;
 		}
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}        
	~String()   
	{ 
		if(_str)
 		{
			delete[] _str;
			_str = nullptr;
 		}
	}   
private:    
	char* _str; 
};
 
void TestString() 
{    
	String s1("hello bit!!!");    
	String s2(s1); 
} 

注意 上述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构 造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内存空间,在释放时同一块 空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。

深拷贝

如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情 况都是按照深拷贝方式提供。

传统写法String类

class String 
{ 
public:    
	String(const char* str = "")    
	{        
		if(nullptr == str)        
		{            
			assert(false);            
			return;        
		}                
		_str = new char[strlen(str) + 1];        
		strcpy(_str, str)}        
	String(const String& s)        
		: _str(new char[strlen(s._str)+1])    
	{            
		strcpy(_str, s._str);    
	}        
	String& operator=(const String& s)    
	{ 
		 if(this != &s)        
		 {            
		 	char* pStr = new char[strlen(s._str) + 1];            
		 	strcpy(pStr, s._str);            
		 	delete[] _str;            
		 	_str = pStr; 
		 }
		 return *this;
	}
	~String()    
	{
		 if(_str)        
		 {            
		 	delete[] _str;            
		 	_str = nullptr;        
		 } 
	}
private:    
	char* _str; 
};

现代写法String类

class String 
{ 
public:    
	String(const char* str = "")    
	{
		 if(nullptr == str)            
		 	str = "";                
		 _str = new char[strlen(str) + 1];        
		 strcpy(_str, str)}        
	 String(const String& s)        
	 	: _str(nullptr)    
	 {        
	 	String strTmp(s._str);        
	 	swap(_str, strTmp);    
	 }    
	String& operator=(String s)    
	{        
		swap(_str, s._str);           
		return *this;    
	}        
	/*    
	String& operator=(const String& s)    
	{        
		if(this != &s)
        {
        	String strTmp(s);           
        	swap(_str, strTmp._str);        
        }                
        return *this;    
    }    
    */        
    ~String()    
    {        
    	if(_str)        
    	{            
    		delete[] _str;            
    		_str = nullptr;        
    	}    
    }    
private:    
	char* _str; 
};

写时拷贝

  • 写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。
  • 引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给 计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,如果计数为1,说明该 对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源。
发布了157 篇原创文章 · 获赞 53 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_42837885/article/details/99191885