string类的模拟之深浅拷贝

浅拷贝:浅拷贝只拷贝指针,但拷贝后两个指针指向同一个内存空间,或者可以说,原对象和拷贝对象共用一个实体,任何一个对象的改变都会引起另一个的改变。当类成员不包括指针何引用时,浅拷贝并无问题;但对于指针与引用成员,当对象的生命周期结束后,浅拷贝会造成同一块内存单元被释放两次,导致内存泄漏。

深拷贝:不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针指向两个不同地址。

调用拷贝构造函数后,浅拷贝依然还有联系,深拷贝的两个对象完全独立。浅拷贝类似于文件创建快捷方式,而深拷贝好比文件复制。编译器默认提供的默认拷贝构造函数是浅拷贝,深拷贝的构造函数需自己实现。

//浅拷贝  
class String  
{  
public:  
    String(const char* pStr = "")//构造函数  
        :_pStr(new char[strlen(pStr)+1])  
    {  
            strcpy(_pStr,pStr);   
    }  
    String(const String& s)//拷贝构造函数  
    {  
        _pStr = s._pStr;  
    }  
    String& operator=(String& s)//赋值运算符重载  
    {  
        if(_pStr != s._pStr)//判断是不是自己给自己赋值  
        {  
            _pStr = s._pStr;  
        }  
        return *this;  
    }  
    ~String()//析构函数  
    {  
        if(NULL != _pStr)  
        {  
            delete []_pStr;  
            _pStr = NULL;  
        }  
    }  
private:  
    char* _pStr;  
};  
  
void test()  
{  
    String s1("abcd");  
    String s2(s1);  
    String s3 = s2;//调用拷贝构造函数(编译器会s2直接初始化s3)  
    String s4;//s4对象已经存在了  
    s4 = s3;//编译器会调用赋值运算符重载将s3的值赋给s4  
}  
int main()  
{  
    test();    
    return 0;  
}  

S1,S2,S3,S4,指针指向的是同一块空间,根据栈帧的开辟原则,先对S4,进行析构,然后S4的空间被释放,S4,接着被置空,接下来堆S3进行析构时,程序崩溃。

既然如此,我们采用深拷贝的方法来构造对象,使得每个对象都拥有独立的内存空间,在内存释放是不会发生多次析构的问题。

class String  //深拷贝
{  
public:  
    String(const char* pStr = "")//构造函数  
        :_pStr(new char[strlen(pStr)+1])  
    {  
            strcpy(_pStr,pStr);  
    }  
    String(const String& s)//拷贝构造  
        :_pStr(new char[strlen(s._pStr)+1])  
    {  
        strcpy(_pStr,s._pStr);  
    }  
    String& operator=(const String& s)//赋值运算符重载  
    {  
        if(_pStr != s._pStr)//判断自赋值  
        {  
            char* temp  = new char[strlen(s._pStr)+1];//先开辟一段新空间  
            strcpy(temp,s._pStr);//将原对象的值赋给新空间  
            delete []_pStr;//释放当前对象  
            _pStr = temp;//将当前对象指向新开辟的空间  
        }  
        return *this;  
    }  
    ~String()//析构  
    {  
        if(NULL != _pStr)  
        {  
            delete[]_pStr;  
            _pStr = NULL;  
        }  
    }  
private:  
    char* _pStr;  
};  
  
void test()  
{  
    String s1("abcd");  
    String s2(s1);  
    String s3 = s2;//调用拷贝构造函数(编译器会s2直接初始化s3)  
    String s4;//s4对象已经存在了  
    s4 = s3;//编译器会调用赋值运算符重载将s3的值赋给s4  
}  
int main()  
{  
    test();  
    system("pause");  
    return 0;  
}  


我们可以看到没个对象都新开辟了一块内存,在析构的时候不会因为析构多次二发生内存泄漏。

深拷贝的现代写法,基本思想利用临时对象来构造出需要拷贝的对象,然后将个对象的内容交换。(可以看做是一种剥夺)
//深拷贝(简洁版2)  
class String  
{  
public:  
    String(const char* pStr = "")//构造函数  
        :_pStr(new char[strlen(pStr)+1])  
    {  
        if(0 == *pStr)  
        {  
            *_pStr = '\0';  
        }  
        else  
        {  
            strcpy(_pStr,pStr);  
        }  
    }  
    String(String& s)//拷贝构造  
        :_pStr(NULL)//防止交换后temp指向随机空间,本函数调用结束导致内存泄漏以致崩溃  
    {  
        String temp(s._pStr);//如果不给出临时变量,交换后s的值将为NULL  
        std::swap(_pStr,temp._pStr);  
    }  
//1  
    String& operator=(const String &s)//赋值运算符重载  
    {  
        if(_pStr != s._pStr)  
        {  
            String temp(s._pStr);//如果不给出临时变量,交换后s的值将为NULL  
            std::swap(_pStr,temp._pStr);  
        }  
        return *this;  
    }  
    /* 2 
    String& operator=(const String& s) 
    { 
    if (this != &s) 
    { 
    String temp(s); 
    std::swap(_pStr, temp._pStr); 
    } 
    return *this; 
    }*/  
  
    /* 3 
    String& operator=(String temp) 
    { 
        std::swap(_pStr, temp._pStr); 
        return *this; 
    }*/  
    ~String()//析构函数  
    {  
        if(NULL == _pStr)  
        {  
            return;  
        }  
        else  
        {  
            delete[]_pStr;  
            _pStr = NULL;  
        }  
    }  
private:  
    char* _pStr;  
};  
  
void test()  
{  
    String s1("abcd");  
    String s2(s1);  
    String s3 = s2;//调用拷贝构造函数(编译器会s2直接初始化s3)  
    String s4;//s4对象已经存在了  
    s4 = s3;//编译器会调用赋值运算符重载将s3的值赋给s4  
}  
int main()  
{  
    test();  
    system("pause");  
    return 0;  
}  
利用深拷贝实现string类的模拟。(完整版)
String.cpp

#include <iostream>
#include <assert.h>
#include <string.h>
#include "String.h"

using namespace std;
现代写法    -----   剥夺    -------利用构造函数创建,然后交换

String::String(const String& s)
:_size(s._size)
, _capacity(s._capacity)
, _str(new char[strlen(s._str) + 1])

{
	strcpy(_str, s._str);
}

//String::String(const String& s)
//:_str(NULL)
//{
//	//构造函数
//	String tmp(s._str);
//	//交换
//	swap(_str, tmp._str);
//}


String& String::operator=(const String& s)
{
	//if (this != &s)//自赋值
	//{
	//	delete[]_str;	//释放原有空间
	//	_str = new char[strlen(s._str) + 1];
	//	strcpy(_str, s._str);
	//	_size = s._size;		
	//	_capacity = s._capacity;
	//}
	//return *this;

	String tmp(s._str);
	swap(_str, tmp._str);
	return *this;

	//swap(_str, s._str);			//不为错,额外的内存开销
	//return *this;

}

String::~String()
{
	if (_str != NULL)
	{
		delete[]_str;
		_str = NULL;			//可置可不置空
		_size = 0;
		_capacity = 0;
	}
}

const char* String::c_str()
{
	return _str;
}


void String::Swap(String& s)		//s1.Swap(s2);
{
	assert(this != &s);
	swap(_str, s._str);
	swap(_size, s._size);
	swap(_capacity, s._capacity);
}

void String::Expand(size_t n)	
{
	if (n > _capacity)
	{

		char* tmp = new char[_capacity];
		strcpy(tmp, _str);
		delete[]_str;
		_str = tmp;
		_capacity = n;
	}
}

void String::PushBack(char ch)
{
	if (_size  >= _capacity)
	{
		Expand(_capacity * 2);
	}
	_str[_size++] = ch;
	_str[_size] = '\0';
}

void String::PushBack(const char* str)
{
	assert(str);
	size_t len = strlen(str);
	if (_size + len > _capacity)
	{
		Expand(_size + len);
	}
	strcpy(_str + _size, str);
	_size += len;
}

void String::PushFront(char ch)
{
	if (_size + 1 > _capacity)	//增容
	{
		Expand(_capacity * 2);
	}
	for (int i = _size + 1; i >= 0; --i)	//搬移
	{
		_str[i + 1] = _str[i];
	}
	_str[0] = ch;
	_size++;
}

void String::PushFront(const char* str)
{
	assert(str);
	size_t len = strlen(str);
	if (len + _size > _capacity)
	{
		Expand(len + _size);
	}
	for (int i = _size; i >= 0; --i)
	{
		_str[i + len] = _str[i];
	}
	memcpy(_str, str, len);

	_size += len;
}

void String::Insert(size_t pos, char ch)
{
	assert(pos < _size);
	if (_size + 1 > _capacity)
	{
		Expand(_capacity * 2);
	}
	for (int i = _size; i >= (int)pos; --i)	//强转pos防止发生无限循环
	{
		_str[i + 1] = _str[i];
	}
	_str[pos] = ch;
	_size++;
}

void String::Insert(size_t pos, const char* str)
{
	assert(str);
	assert(pos <= _size);
	size_t len = strlen(str);
	if (len + _size > _capacity)
	{
		Expand(_size + len);
	}
	for (int i = _size; i >= (int)pos; --i)
	{
		_str[i + len] = _str[i];
	}
	while (*str != '\0')
	{
		_str[pos++] = *(str++);
	}
	_size += len;
}

void String::Erase(size_t pos, size_t n)
{
	//1.pos
	assert(pos < _size);
	//pos+n
	if (pos + n >= _size)
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		strcpy(_str + pos, _str + pos + n);
		_size -= n;
	}
}

size_t String::Find(char ch, size_t pos)
{
	for (size_t i = pos; i < _size; i++)
	{
		if (ch == _str[i]) return i;
	}
	return -1;
}

size_t String::Find(const char* str, size_t pos)
{
	assert(str != NULL);
	assert(pos < _size);
	assert(_size > strlen(str));
	const char* src = _str + pos;

	while (*src != '\0')//标记母串的查找位置
	{
		const char* match = str;
		const char* cur = src;
		while (*match != '\0' && *match == *cur)//子串没有走到结尾
		//子串的当前字符不等于母串
		{
				match++;
				cur++;
		}
		if (*match == '\0')
		{
			return src - _str;
		}
		src++;
	}
	return -1;
}

void String::Replace(size_t pos, int len, const char* sub2)
{
	//pos > _size  == _size   <  _size
	//len > strlen(sub2)              pos + len > size
	//sub2
}



bool String::operator>(const String& s)const
{
	size_t i = 0;
	while (_str[i] != '\0' && s._str[i] != '\0')
	{
		if (_str[i] == s._str[i])
			i++;
		else if (_str[i] > s._str[i])
			return true;
		else
			return false;
	}
	if (_str[i] != '\0')
		return true;
	else
		return false;
}

bool String::operator>=(const String& s)const
{
	return *this > s || *this == s;
}

bool String::operator<(const String& s)const
{
	return !(*this >= s);
}

bool String::operator<=(const String& s)const
{
	return !(*this > s);
}

bool String::operator==(const String& s)const
{
	size_t i = 0;
	while (_str[i] != '\0' && s._str[i] != '\0')
	{
		if (_str[i] == s._str[i])
			i++;
		else
			return false;
	}
	if (_str[i] == '\0' && s._str[i] == '\0')
		return true;
	else
		return false;
}

bool String::operator!=(const String& s)const
{
	return !(*this == s);
}

String String::operator+(char ch)
{
	String tmp(*this);		//构造
	tmp.PushBack(ch);			
	
	return tmp;
}

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

String String::operator+(const char* str)
{
	String tmp(*this);
	tmp.PushBack(str);

	return tmp;
}

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

void TestString()
{
	String s1("change world");
	cout << s1.Find("wor")<<endl;

}

int main()
{
	TestString();
	return 0;
}
String.h
#pragma
#include <string.h>

#define NULL 0

class String
{
public:

	String(const char* str = "")
	:_str(new char[strlen(str) + 1])	
	, _size(strlen(str))
	, _capacity(strlen(str))		//size和capacity保持一致'\0'不算给_capacity
	{
		strcpy(_str, str);
	}

	String(const String& s);
	String& operator=(const String& s);
	~String();

	const char* c_str();

	void Swap(String& s);
	void Expand(size_t n);
	void PushBack(char ch);
	void PushBack(const char* str);
	void PushFront(char ch);
	void PushFront(const char* str);

	void Insert(size_t pos, char ch);
	void Insert(size_t pos, const char* str);
	void Erase(size_t pos, size_t n = 1);
	void Replace(size_t pos, int len, const char* sub2);


	size_t Find(char ch, size_t pos);
	size_t Find(const char* str, size_t pos = 0);

	String operator+(char ch);
	String& operator+=(char ch);
	String operator+(const char* str);
	String& operator+=(const char* str);


	bool operator>(const String& s)const;
	bool operator>=(const String& s)const;
	bool operator<(const String& s)const;
	bool operator<=(const String& s)const;
	bool operator==(const String& s)const;
	bool operator!=(const String& s)const;

private:
	size_t _size;
	size_t _capacity;
	char* _str;

};











猜你喜欢

转载自blog.csdn.net/bian_cheng_ru_men/article/details/79797763