C++之深浅拷贝及String类基本操作

今天我来介绍一下C++中的深浅拷贝问题。

一。浅拷贝

1.定义:也称位拷贝,编译器只是将对象中的值采用基本类型值复制的方式拷贝过来,如果对象中管理资源,最后就会导致
多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进行操作时,就会发生访问违规问题。

2.举例:

class String
{
public:
	String(const char* str="")                     //构造函数
		:_pstr(new char[strlen(str)+1])        //初始化列表
	{
		strcpy(_pstr,str);                     //拷贝
		cout<<"string::string()"<<endl;        //打印
	
	}
	~String()                                      //析构函数
	{
		if(_pstr)
		{
			delete[] _pstr;
			_pstr==NULL;
			cout<<"string::~string()"<<endl;
		}
	}
private:
	char* _pstr;
};

int main()
{
	String s1("hello");                            //调用构造函数创建对象。
	String s2(s1);                                 //调用拷贝构造函数创建对象。
	
	return 0;

}

这个就是浅拷贝,当类的成员变量是指针时,因为没有自己写出拷贝构造函数,系统会自动生成,而此时系统只会拷贝内容,所以两个对象占用同一资源在同一块地址上,这样在释放时,会发生多次释放导致内存访问违规。所以说浅拷贝只是对类中的简单数据成员进行赋值,当类的成员变量存在指针成员时,就会出现问题,他只会拷贝指针,并不会改变地址。这就是浅拷贝。

二。深拷贝:

1.当成员为指针时,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针,这样就不会产生问题了。

2.举例:(1)传统法深拷贝:

class String
{
public:
	String(const char* str="")         //构造函数
	{
		if(str==NULL)              //若为空初始化为空。
		{
			str="";
		}
		_pstr=new char[strlen(str)+1];  //否则进行拷贝。
		strcpy(_pstr,str);
		cout<<"string::string()"<<endl;
	
	}
	~String()                                //析构函数。
	{
		if(_pstr)                        //若不为空,进行销毁。
		{
			delete[] _pstr;
			_pstr==NULL;
			cout<<"string::~string()"<<endl;
		}
	}
	String(const String& s)                    //拷贝构造函数。
		:_pstr(new char[strlen(s._pstr)+1])  
	{
		strcpy(_pstr,s._pstr);
	}

	String& operator=(const String& s)         //赋值运算符重载函数
	{
		if(this!=&s)                       //当两者不相等时进行赋值。
		{
			char* t=new char[strlen(s._pstr)+1];   
			strcpy(t,s._pstr);                     
			delete[] _pstr;                        
			_pstr=t;                               
		}
		return *this;
	}
private:
	char* _pstr;
};

int main()
{
	String s1("hello");
	String s2(s1);
	
	return 0;

}

(2)简洁版深拷贝:

class String
{
public:
	String(const char* str="")
	{
		if(str==NULL)
		{
			str="";
		}
		_pstr=new char[strlen(_pstr)+1];
		strcpy(_pstr,str);
	}

	~String()
	{
		if(_pstr)
		{
			delete[] _pstr;
			_pstr==NULL;
		}
	}

	String(const String& s)
		:_pstr(NULL)
	{
		String t(s._pstr);
		swap(_pstr,s._pstr);
	}

	String& operator=(const String& s)
	{
		String t(s);
		swap(s._pstr,t._pstr);
		return *this;
	}

private:
	char* _pstr;
};
int main()
{
	String s1("hello");
	String s2(s1);
	
	return 0;

}

在深拷贝中,我们自己定义了拷贝构造函数,所以系统不会再用默认的函数,此时他就会开辟一个新的地址空间,所以此时两个对象指的地址不相同,就不会产生内存访问违规现象了。

3.所以总的来说:当类中有指针成员变量时,尽量自己写拷贝构造函数,这样就不会造成浅拷贝的问题了。

三。String类的增删改查:

//String类的各种操作:
class String
{
public:
	String(const char* str="")            //构造函数
	{
		_size=_capacity=strlen(str);
		_str=new char[strlen(str)+1];
		_capacity=_size;
		strcpy(_str,str);
	}
	~String()                            //析构函数
	{
		if(_str)
		{
			delete[] _str;
			_size=_capacity=0;
			_str=NULL;
		}
	}
	void Swap(String& s)                  //交换
	{
		swap(_str,s._str);
		swap(_size,s._size);
		swap(_capacity,s._capacity);
	}
	String(const String& s)               //拷贝构造函数
		:_str(NULL)
		,_size(0)
		,_capacity(0)
	{
		String t(s._str);
		Swap(t);
	}

	String& operator=(String s)           //赋值运算符重载
	{
		Swap(s);
		return *this;
	}
	void expand(size_t n)                  //扩容函数
	{
		if(n>_capacity)
		{
			char* t=new char[n+1];
			strcpy(t,_str);
			delete[] _str;
			_str=t;
			_capacity=n;
		}
	}
	void PushBack(char ch)                //尾插字符
	{
		if(_size==_capacity)
		{
			expand(_capacity*2);
		}
		_str[_size]=ch;
		_size++;
		_str[_size]='\0';
	}
	void PushBack(const char* s)          //尾插字符串
	{
		size_t len=strlen(s);
		if(len>_capacity)
		{
			expand(_capacity*2);
		}
		strcpy(_str+_size,s);
		_size=_size+len;
	}

	void PopBack()                      //尾删
	{
		if(_size==0)
		{
			return;
		}
		_size--;
		_str[_size]='\0';
	}
	void PushFront(char ch)             //头插字符
	{
		int i=0;
		if(_size==_capacity)
		{
			expand(2*_capacity);
		}
		for(i=_size;i>=0;i--)
		{
			_str[i+1]=_str[i];
		}
		_str[0]=ch;
		_size++;
	}

	void PushFront(const char* s)           //头插字符串
	{
		char *p=_str;
		size_t len=strlen(s);
		int i=0;
		if(_size+len>=_capacity)
		{
			expand(2*_capacity);
		}
		for(i=_size;i>=0;i--)
		{
			_str[i+len]=_str[i];
		}
		while(*s)
		{
			*p++=*s++;
		}
		_size+=len;
	}

	void PopFront()                      //头删
	{
		int i=0;
		if(_size==0)
		{
			return;
		}
		if(_size==1)
		{
			_size--;
			return;
		}
		for(i=0;i<_size;i++)
		{
			_str[i]=_str[i+1];
		}
		_size--;
	}

	void Insert(size_t pos, char ch)          //任意位置插入字符
	{
		int i=0;
		if(_size==_capacity)
		{
			expand(2*_capacity);
		}
		for(i=_size;i>(int)pos;i--)
		{
			_str[i+1]=_str[i];
		}
		_str[pos+1]=ch;
		_size++;
	}
	void Insert(size_t pos,const char* s)          //任意位置插入字符串
	{
		int i=0;
		size_t len=strlen(s);
		if(_size+len>_capacity)
		{
			expand(2*_capacity);
		}
		for(i=_size;i>pos;i--)
		{
			_str[i+len]=_str[i];
		}
		while(*s)
		{
			_str[++pos]=*s++;
		}
		_size+=len;
	}

	void Erase(size_t pos, size_t n = 1)         //删除指定位置后的n个数
	{
		if(pos+n>_size-1)
		{
			_str[pos]='\0';
			_size=pos;
		}
		else{
			strcpy(_str+pos,_str+pos+n);
			_size-=n;
		}
	}

	size_t Find(char ch)                      //查询字符函数
	{
		size_t i=0;
		for(i=0;i<_size;i++)
		{
			if(_str[i]==ch)
			{
				return i;
			}
		}
		return 0;
	}
        void Replace(char ch1, char ch2)        //用ch2替换ch1的函数
	{
		int i=0;
		for(i=0;i<_size;i++)
		{
			if(_str[i]==ch1)
			{
				_str[i]=ch2;
			}
		}
	}
bool operator<(const String& s) const     //小于号重载
	{
		int i,j;
		int a=_size;
		int b=s._size;
		for(i=0,j=0;i<a && j<b;i++,j++)
		{
			if(_str[i]>s._str[j])
			{
				return 0;
			}
		}
		if(i==a && j<b)
		{
			return 1;
		}
		else{
			return 0;
		}
	}

	bool operator==(const String& str) const        //等号重载
	{
		int i=0;
		if(_size!=str._size)
		{
			return 0;
		}
		for(i=0;i<_size;i++)
		{
			if(_str[i]!=str._str[i])
			{
				return 0;
			}
		}
		return 1;
	}

	bool operator>(const String& str) const        //大于号重载
	{
		return (!(*this<str) && !(*this==str));
	}
	bool operator>=(const String& str) const        //大于等于重载
	{
		return ((*this>str)&&(*this==str));
	}

	bool operator<=(const String& str) const       //小于等于重载
	{
		return !(*this>str);
	}

	char& operator[](size_t pos)                  //[]的重载。
	{
		if(pos>_size)
		{
			return _str[_size];
		}
		return _str[pos];
	}

	const char * c_str()                    
	{
		return _str;
	}

	const size_t Size()
	{
		return _size;
	}
private:
	char *_str;
	size_t _size;
	size_t _capacity;
};

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值