Error[8]: Undefined offset: 1776, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

文章目录

1.浅拷贝问题解决 1.1浅拷贝存在的问题


对于浅拷贝,主要存在于拷贝构造和赋值运算符重载的过程中,下面给出一段代码结合分析

#pragma warning(disable:4996)
#include

using namespace std;

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

	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}

private:
	char* _str;
};

void StringTest()
{
	String s1("Hello");
	String s2(s1);
}

int main()
{
	StringTest();
	return 0;
}

执行时程序直接崩溃


下面分析一下原因:
上述的崩溃时由于浅拷贝导致的多次释放问题

再来看下面的代码快:

分析:
本次报错是因为执行s2 = s1时,导致内存泄漏以及多次释放的问题,具体看下面的分析

总结一下:
浅拷贝会导致①内存泄露②多次释放同一块空间
这些错误都是极其严重的,我们务必要避免!

接下来就来探讨一下如何解决这些问题:

1.2通过深拷贝的方式解决浅拷贝问题

本质:让每一个对象都拥有一份独立的资源

传统版解决方式
  1. 解决拷贝构造
  2. 解决赋值运算符重载

    下面给出解决的完整代码
#pragma warning(disable:4996)
#include

using namespace std;

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(new char[strlen(s._str)+1])
	{
		strcpy(_str, s._str);
	}

	String& operator=(const String& s)
	{
		if (this != &s)
		{
			char* temp = new char[strlen(s._str) + 1];
			strcpy(temp, s._str);
			delete[] _str;
			_str = temp;
		}
		return *this;
	}

	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}

private:
	char* _str;
};

void StringTest()
{
	String s1("Hello");
	String s2("World");
	s2 = s1;

	//String s2(s1);
}

int main()
{
	StringTest();
	return 0;
}
现代版解决方式

仔细观察上面的代码,我们发现其实重复的 *** 作很多,比如每次申请新空间,拷贝元素。



现代版的方式就是采用巧妙地代码复用,将繁琐的 *** 作简洁化
具体修改在拷贝构造和赋值运算符重载处

下面我们一一解决

  1. 拷贝构造的优化
  2. 赋值运算符重载的优化

    以上便是对之前代码的一些优化!
1.3通过写时拷贝解决 理解写时拷贝方法

写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。



引用计数:用来记录资源使用者的个数。



在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,
当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,
如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;
否则就不能释放,因为还有其他对象在使用该资源。



下面使用图解的方式解释一下:

OK,看了上面的解法,有没有感觉有点问题呢?
上面的过程并没有涉及到对象内容修改,假设我现在想将上面s2对象的内容修改为“World”;由于s1 s2共用一块内存空间,所以修改s2的同时s1的内容也被修改了
但是很明显,这不是我的本意,也不符合规矩。


那如何解决这个棘手问题呢?

其实上面的这个过程还没有涉及到我们的写时拷贝。



所谓的写时拷贝是在上面的基础上解决修改(写)内容的时候发生的问题
具体步骤如下:
假设我们现在已经处于上面图示的场景,即s1和s2共用同一块内存空间,计数器此时为2
现在我想要将s2的内容改为“World”,需要执行以下步骤:
(1)为s2对象开辟新的空间
(2)将原来的空间的计数器值减减
(3)将s1对象的内容拷贝至为s2新开辟的空间中,并将s2中计数器值设置为1
(4)s2在新空间内进行修改 *** 作
下面通过图示的方式再次演示该过程:

以上便是写时拷贝的粗略思路,具体细节内容等到后续总结,目前处于扫盲状态。


1.4验证不同平台的string类是通过什么方式解决浅拷贝的

验证思路:
(1)实例化一个对象s1,长度大于15;
注意:长度必须大于15,否则无法测试出正确的结果。


因为在Windows下,string类中维护着一个空间为16的字符数组,因此小于等于15的字符串直接被存储在数组内,并不会申请空间,也就无法验证。



(2)通过拷贝构造实例化对象s2
(3)打印s1和s2对象的地址,观察地址值是否一样
如果一样 -----> 写时拷贝
如果不一样 ----> 深拷贝
验证代码如下:

void TestCopy()
{
	string s1(20, 'A');
	string s2(s1);

	printf("&s1 is %p\n",s1.c_str());
	printf("&s2 is %p\n", s2.c_str());
}

int main()
{
	TestCopy();
	return 0;
}

1、在Windows平台下的VS2013环境中

结论:vs2013中string是按照深拷贝实现的
2、在Linux平台下

结论:Linux中string是按照写时拷贝实现的

2、string类实现

有了前面的知识做铺垫,这里直接给出代码,模拟实现只是对主要的方法进行模拟,并不是完全实现一个string容器

//mystring.h
#pragma once

#include

namespace gyj
{
	class string
	{
		friend std::ostream& operator<<(std::ostream& _cout, const gyj::string& s);
	public:
		typedef char* iterator;
	public:
		/构造和析构///
		string(const char* s = "");
		string(const string& str);
		string& operator=(const string& str);
		~string();
		///迭代器相关/
		iterator begin();
		iterator end();
		///容量相关//
		size_t size()const;
		size_t capacity()const;
		bool empty()const;
		void resize(size_t n, char c = ')';void
		reserve ()size_t n;///元素访问相关//

		char
		&operator []()size_t index;const
		char &operator []()size_t indexconst;///修改相关的///
		void
		push_back (char) c;&
		stringoperator +=(char) c;void
		append (constchar *) str;&
		stringoperator +=(constchar *) str;void
		clear ();void
		swap (&string) s;const
		char *c_str ()const;/其他//
		bool
		operator <(const& string) s;bool
		operator <=(const& string) s;bool
		operator (>const& string) s;bool
		operator (>=const& string) s;bool
		operator ==(const& string) s;bool
		operator !=(const& string) s;// 返回c在string中第一次出现的位置


		find
		size_t (char, c= size_t pos 0 )const ;// 返回子串s在string中第一次出现的位置
		find
		size_t (constchar *, s= size_t pos 0 )const ;// 在pos位置上插入字符c/字符串str,并返回该字符的位置
		&
		stringinsert (,size_t poschar ) c;&
		stringinsert (,size_t posconst char *) str;// 删除pos位置上的元素,并返回该元素的下一个位置


		&
		stringerase (,size_t pos) size_t len;private
	:char
		*; _str;
		size_t _size;
		size_t _capacity}
	;}

extern
void stringTest1 ();extern
void stringTest2 ();#

接下来是mystring.cpp

pragmawarning (:disable4996)#
include#
include"mystring.h"#
include::

gyj::stringstring(constchar *) sif
{
	( nullptr== ) sassert
	{
		(0);}
	=
	_size strlen ()s;=
	_capacity ; _size=
	_str new char [+_capacity1 ];strcpy
	(,_str) s;}
::
gyj::stringstring(const& string) str:
_str(nullptr),
_size (0),
_capacity (0)temp
{
	string (.str)_str;::
	stdswap(,_str. temp)_str;=
	_size . str;_size=
	_capacity . str;_capacity}
::

gyj&string:: gyj::stringoperator=(const& string) strif
{
	( this!= & )strtemp
	{
		string (.str)_str;::
		stdswap(,_str. temp)_str;=
		_size . str;_size=
		_capacity . str;_capacity}
	return
	* this;}
::

gyjstring::~string()if
{
	( )_strdelete
	{
		[]; _str=
		_str nullptr ;}
	}
///迭代器相关/
::
gyj::string::iterator gyj::stringbegin()return
{
	; _str}
::

gyj::string::iterator gyj::stringend()return
{
	+ _str ; _size}
///容量相关//

::
size_t gyj::stringsize()constreturn
{
	; _size}
::
size_t gyj::stringcapacity()constreturn
{
	; _capacity}
bool

:: gyj::stringempty()constif
{
	( 0== ) _sizereturn
	{
		true ;}
	return
	false ;}
void
:: gyj::stringresize(,size_t nchar ) cif
{
	( )n > _sizeif
	{
		( )n > _capacityreserve
		{
			()n;}
		memset
		(+_str , _size, c- n ) _size;}
	=
	_size ; n[
	_str]_size= ';' }void
::
:: gyjreservestring()ifsize_t n(
{
	) charn > _capacity*
	{
		=new temp char [ +1n ] ;strcpy(
		,)temp; _strdelete[
		];= _str;
		_str = temp;
		_capacity } n}
	///元素访问相关//
char
&
:::: gyjoperatorstring[]()assertsize_t index(
{
	<)index ; _sizereturn[
	] _str;index}const
char
& :::: gyjoperatorstring[]()constsize_t indexassert(
{
	<)index ; _sizereturn[
	] _str;index}///修改相关的///
void
::
:: gyjpush_backstring(char)if c(
{
	== )_size //扩容 _capacityreserve
	{
		(
		*2_capacity ) ;}[
	++
	_str]_size=; [ c]
	_str=_size';' } ::&
::
gyj::stringoperator gyj+=string(char)push_back( c)
{
	;returnc*this
	; }void::
::
append gyj(stringconstchar*) if( str+
{
	strlen (_size))//扩容str= >= _capacity*
	{
		2
		size_t newcapacity + _capacity strlen ( > _size ) ?*str2 : _capacity + strlen ( _size ) +1str; reserve ()
		;}newcapacitystrcat(
	,
	);_str+= strstrlen(
	_size ) ;}str::&
::
gyj::stringoperator gyj+=string(constchar*) append( str)
{
	;returnstr*this
	; }void::
::
clear gyj(string)[0]
{
	_str=';'= 0 ;}
	_size void ::::
swap
( gyj&string)::swapstring( s,
{
	std.);_str:: sswap_str(,
	std.);_size:: sswap_size(,
	std.);_capacity} sconst_capacitychar*
::
:: c_str( gyj)stringconstreturn;}// <<重载
{
	:: _str&
::




operator
std<<ostream( gyj::&,conststd::ostream& _cout) << gyj.string; sreturn
{
	_cout ; s}_str/其他//

	bool _cout::
::

operator
< gyj(stringconst&)int= stringstrcmp s(
{
	, ret . );_strif s(_strret<
	0 )return true ;}
	{
		return false;
	}
	bool ::::
operator
<= gyj(stringconst&)return! string( s*
{
	this );}bool>s::::
operator
( gyjconststring&)>int= stringstrcmp s(
{
	, ret . );_strif s(_str0)
	return trueret > ;}
	{
		return false;
	}
	bool ::::
operator
( gyjconststring&)>=return! string( s*
{
	this <);} bool s::::
operator
== gyj(stringconst&)int= stringstrcmp s(
{
	, ret . );_strif s(_str==0
	) returnret true ;}
	{
		return false;
	}
	bool ::::
operator
!= gyj(stringconst&)return! string( s*
{
	this ==);} // 返回c在string中第一次出现的位置 s::::
find

(
size_t gyjcharstring,)constfor c( size_t pos= ;
{
	< ;size_t i ++ pos) i if _size( i==[
	{
		] )c return _str;i}}
		{
			return i-
		1
	;
	} // 返回子串s在string中第一次出现的位置::::
find
(
size_t gyjconststringchar*,) constassert s( size_t pos) ;
{
	assert(s<)
	;constpos char _size*=
	+ ;while src ( _str * pos)
	const char*src=
	{
		; constchar match * s=
		; while( cur * src&&
		* ==*match ) ++match ; ++cur;
		{
			}matchif
			(cur*
		==
		')' return-match ; }else
		{
			++ src ; _str}
		}
		return
		{
			-src1
		;
	}
	// 在pos位置上插入字符c/字符串str,并返回该字符的位置 ::&::
::
insert
gyj(string, gyjcharstring)assert(size_t pos<= ) c;
{
	if(pos == _size)//扩容
	char *_size = _capacitynew
	{
		char
		[ *newstr 2 + 1]_capacity ; strcpy ( ,);
		delete[newstr] _str;=
		;*=2 _str;
		_str } newstr//移数据
		_capacity for (int
	=
	;
	( int) i ; _size-- i >= )[+pos1 ]i=
	{
		_str[i ] ;} [ _str]i=;

	++
	_str;posreturn * cthis
	_size;}

	:: &::::

insert
gyj(string, gyjconststringchar*)size_t pos= strlen () str;
{
	size_t len if (+str)//扩容
	//扩容 char_size * len > _capacity=new
	{
		char
		[ *newstr 2 + 1]_capacity ; strcpy ( ,);
		delete[newstr] _str;=
		;*=2 _str;
		_str } newstr//后移数据
		_capacity for (int
	=

	;
	( int) i ; _size-- i >= )[+pos] =i[
	{
		_str]len ; i} //拷贝字符串 _strwhilei(*
	!=

	')'
	[ ++]str = *++
	{
		_str;pos}+= ; returnstr*this
	;
	_size } len// 删除pos位置上的元素,并返回该元素的下一个位置
	:: &::::
erase

(
gyj,string) gyjassertstring(<)size_t pos; size_t lenif
{
	(+pos ) _size//pos位置之后全为0[

	] =pos ';' len >= _size=;
	{
		_str}poselse strcpy (+
		_size , pos+
	+
	)
	{
		;-=_str ; pos} _str return pos * lenthis;
		_size } lenvoid
	stringTest1
	( )::s1

(


"hello" );::
{
	gyjs2string ();::;
	gyj=string ;::s1<<.
	gyjsizestring s3(
	s3 ) s2<<

	std::cout ;s1::<<.capacity ( std)endl<<
	std::cout ; s1::<<.empty ( std)endl<<
	std::cout ; s1.resize(3 ) std;endl.

	s1resize(10);.
	s2resize(15,'A')
	s3;.reserve(4 );.

	s1reserve(20);.
	s2reserve(100);::
	s3<<[0]<<::

	std;cout :: s1<<[3 ] std<<endl::
	std;cout const s2::s4( "aaaaaa" std)endl;
	char gyj=string [3];//_CrtDumpMemoryLeaks();
	} ch void s4stringTest2()::

	s1
(

"Hello" );.
{
	gyjpush_backstring (' ');.

	s1append("World");+=
	s1'!';+="~~~~";::
	s1 << .c_str
	s1 ( )<<
	
	std::cout ; s1::s2("12345" ) std;endl.

	gyjswapstring ();.clear
	s1();s2_CrtDumpMemoryLeaks(
	s1);}[+++][+++]
	[+++][+++][+++][+++]

[+++]

具体代可以参考我的码云

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 1777, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

文章目录

1.浅拷贝问题解决 1.1浅拷贝存在的问题


对于浅拷贝,主要存在于拷贝构造和赋值运算符重载的过程中,下面给出一段代码结合分析

#pragma warning(disable:4996)
#include

using namespace std;

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

	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}

private:
	char* _str;
};

void StringTest()
{
	String s1("Hello");
	String s2(s1);
}

int main()
{
	StringTest();
	return 0;
}

执行时程序直接崩溃


下面分析一下原因:
上述的崩溃时由于浅拷贝导致的多次释放问题

再来看下面的代码快:

分析:
本次报错是因为执行s2 = s1时,导致内存泄漏以及多次释放的问题,具体看下面的分析

总结一下:
浅拷贝会导致①内存泄露②多次释放同一块空间
这些错误都是极其严重的,我们务必要避免!

接下来就来探讨一下如何解决这些问题:

1.2通过深拷贝的方式解决浅拷贝问题

本质:让每一个对象都拥有一份独立的资源

传统版解决方式
  1. 解决拷贝构造
  2. 解决赋值运算符重载

    下面给出解决的完整代码
#pragma warning(disable:4996)
#include

using namespace std;

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(new char[strlen(s._str)+1])
	{
		strcpy(_str, s._str);
	}

	String& operator=(const String& s)
	{
		if (this != &s)
		{
			char* temp = new char[strlen(s._str) + 1];
			strcpy(temp, s._str);
			delete[] _str;
			_str = temp;
		}
		return *this;
	}

	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}

private:
	char* _str;
};

void StringTest()
{
	String s1("Hello");
	String s2("World");
	s2 = s1;

	//String s2(s1);
}

int main()
{
	StringTest();
	return 0;
}
现代版解决方式

仔细观察上面的代码,我们发现其实重复的 *** 作很多,比如每次申请新空间,拷贝元素。



现代版的方式就是采用巧妙地代码复用,将繁琐的 *** 作简洁化
具体修改在拷贝构造和赋值运算符重载处

下面我们一一解决

  1. 拷贝构造的优化
  2. 赋值运算符重载的优化

    以上便是对之前代码的一些优化!
1.3通过写时拷贝解决 理解写时拷贝方法

写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。



引用计数:用来记录资源使用者的个数。



在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,
当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,
如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;
否则就不能释放,因为还有其他对象在使用该资源。



下面使用图解的方式解释一下:

OK,看了上面的解法,有没有感觉有点问题呢?
上面的过程并没有涉及到对象内容修改,假设我现在想将上面s2对象的内容修改为“World”;由于s1 s2共用一块内存空间,所以修改s2的同时s1的内容也被修改了
但是很明显,这不是我的本意,也不符合规矩。


那如何解决这个棘手问题呢?

其实上面的这个过程还没有涉及到我们的写时拷贝。



所谓的写时拷贝是在上面的基础上解决修改(写)内容的时候发生的问题
具体步骤如下:
假设我们现在已经处于上面图示的场景,即s1和s2共用同一块内存空间,计数器此时为2
现在我想要将s2的内容改为“World”,需要执行以下步骤:
(1)为s2对象开辟新的空间
(2)将原来的空间的计数器值减减
(3)将s1对象的内容拷贝至为s2新开辟的空间中,并将s2中计数器值设置为1
(4)s2在新空间内进行修改 *** 作
下面通过图示的方式再次演示该过程:

以上便是写时拷贝的粗略思路,具体细节内容等到后续总结,目前处于扫盲状态。


1.4验证不同平台的string类是通过什么方式解决浅拷贝的

验证思路:
(1)实例化一个对象s1,长度大于15;
注意:长度必须大于15,否则无法测试出正确的结果。


因为在Windows下,string类中维护着一个空间为16的字符数组,因此小于等于15的字符串直接被存储在数组内,并不会申请空间,也就无法验证。



(2)通过拷贝构造实例化对象s2
(3)打印s1和s2对象的地址,观察地址值是否一样
如果一样 -----> 写时拷贝
如果不一样 ----> 深拷贝
验证代码如下:

void TestCopy()
{
	string s1(20, 'A');
	string s2(s1);

	printf("&s1 is %p\n",s1.c_str());
	printf("&s2 is %p\n", s2.c_str());
}

int main()
{
	TestCopy();
	return 0;
}

1、在Windows平台下的VS2013环境中

结论:vs2013中string是按照深拷贝实现的
2、在Linux平台下

结论:Linux中string是按照写时拷贝实现的

2、string类实现

有了前面的知识做铺垫,这里直接给出代码,模拟实现只是对主要的方法进行模拟,并不是完全实现一个string容器

//mystring.h
#pragma once

#include

namespace gyj
{
	class string
	{
		friend std::ostream& operator<<(std::ostream& _cout, const gyj::string& s);
	public:
		typedef char* iterator;
	public:
		/构造和析构///
		string(const char* s = "");
		string(const string& str);
		string& operator=(const string& str);
		~string();
		///迭代器相关/
		iterator begin();
		iterator end();
		///容量相关//
		size_t size()const;
		size_t capacity()const;
		bool empty()const;
		void resize(size_t n, char c = ')';void
		reserve ()size_t n;///元素访问相关//

		char
		&operator []()size_t index;const
		char &operator []()size_t indexconst;///修改相关的///
		void
		push_back (char) c;&
		stringoperator +=(char) c;void
		append (constchar *) str;&
		stringoperator +=(constchar *) str;void
		clear ();void
		swap (&string) s;const
		char *c_str ()const;/其他//
		bool
		operator <(const& string) s;bool
		operator <=(const& string) s;bool
		operator (>const& string) s;bool
		operator (>=const& string) s;bool
		operator ==(const& string) s;bool
		operator !=(const& string) s;// 返回c在string中第一次出现的位置


		find
		size_t (char, c= size_t pos 0 )const ;// 返回子串s在string中第一次出现的位置
		find
		size_t (constchar *, s= size_t pos 0 )const ;// 在pos位置上插入字符c/字符串str,并返回该字符的位置
		&
		stringinsert (,size_t poschar ) c;&
		stringinsert (,size_t posconst char *) str;// 删除pos位置上的元素,并返回该元素的下一个位置


		&
		stringerase (,size_t pos) size_t len;private
	:char
		*; _str;
		size_t _size;
		size_t _capacity}
	;}

extern
void stringTest1 ();extern
void stringTest2 ();#

接下来是mystring.cpp

pragmawarning (:disable4996)#
include#
include"mystring.h"#
include::

gyj::stringstring(constchar *) sif
{
	( nullptr== ) sassert
	{
		(0);}
	=
	_size strlen ()s;=
	_capacity ; _size=
	_str new char [+_capacity1 ];strcpy
	(,_str) s;}
::
gyj::stringstring(const& string) str:
_str(nullptr),
_size (0),
_capacity (0)temp
{
	string (.str)_str;::
	stdswap(,_str. temp)_str;=
	_size . str;_size=
	_capacity . str;_capacity}
::

gyj&string:: gyj::stringoperator=(const& string) strif
{
	( this!= & )strtemp
	{
		string (.str)_str;::
		stdswap(,_str. temp)_str;=
		_size . str;_size=
		_capacity . str;_capacity}
	return
	* this;}
::

gyjstring::~string()if
{
	( )_strdelete
	{
		[]; _str=
		_str nullptr ;}
	}
///迭代器相关/
::
gyj::string::iterator gyj::stringbegin()return
{
	; _str}
::

gyj::string::iterator gyj::stringend()return
{
	+ _str ; _size}
///容量相关//

::
size_t gyj::stringsize()constreturn
{
	; _size}
::
size_t gyj::stringcapacity()constreturn
{
	; _capacity}
bool

:: gyj::stringempty()constif
{
	( 0== ) _sizereturn
	{
		true ;}
	return
	false ;}
void
:: gyj::stringresize(,size_t nchar ) cif
{
	( )n > _sizeif
	{
		( )n > _capacityreserve
		{
			()n;}
		memset
		(+_str , _size, c- n ) _size;}
	=
	_size ; n[
	_str]_size= ';' }void
::
:: gyjreservestring()ifsize_t n(
{
	) charn > _capacity*
	{
		=new temp char [ +1n ] ;strcpy(
		,)temp; _strdelete[
		];= _str;
		_str = temp;
		_capacity } n}
	///元素访问相关//
char
&
:::: gyjoperatorstring[]()assertsize_t index(
{
	<)index ; _sizereturn[
	] _str;index}const
char
& :::: gyjoperatorstring[]()constsize_t indexassert(
{
	<)index ; _sizereturn[
	] _str;index}///修改相关的///
void
::
:: gyjpush_backstring(char)if c(
{
	== )_size //扩容 _capacityreserve
	{
		(
		*2_capacity ) ;}[
	++
	_str]_size=; [ c]
	_str=_size';' } ::&
::
gyj::stringoperator gyj+=string(char)push_back( c)
{
	;returnc*this
	; }void::
::
append gyj(stringconstchar*) if( str+
{
	strlen (_size))//扩容str= >= _capacity*
	{
		2
		size_t newcapacity + _capacity strlen ( > _size ) ?*str2 : _capacity + strlen ( _size ) +1str; reserve ()
		;}newcapacitystrcat(
	,
	);_str+= strstrlen(
	_size ) ;}str::&
::
gyj::stringoperator gyj+=string(constchar*) append( str)
{
	;returnstr*this
	; }void::
::
clear gyj(string)[0]
{
	_str=';'= 0 ;}
	_size void ::::
swap
( gyj&string)::swapstring( s,
{
	std.);_str:: sswap_str(,
	std.);_size:: sswap_size(,
	std.);_capacity} sconst_capacitychar*
::
:: c_str( gyj)stringconstreturn;}// <<重载
{
	:: _str&
::




operator
std<<ostream( gyj::&,conststd::ostream& _cout) << gyj.string; sreturn
{
	_cout ; s}_str/其他//

	bool _cout::
::

operator
< gyj(stringconst&)int= stringstrcmp s(
{
	, ret . );_strif s(_strret<
	0 )return true ;}
	{
		return false;
	}
	bool ::::
operator
<= gyj(stringconst&)return! string( s*
{
	this );}bool>s::::
operator
( gyjconststring&)>int= stringstrcmp s(
{
	, ret . );_strif s(_str0)
	return trueret > ;}
	{
		return false;
	}
	bool ::::
operator
( gyjconststring&)>=return! string( s*
{
	this <);} bool s::::
operator
== gyj(stringconst&)int= stringstrcmp s(
{
	, ret . );_strif s(_str==0
	) returnret true ;}
	{
		return false;
	}
	bool ::::
operator
!= gyj(stringconst&)return! string( s*
{
	this ==);} // 返回c在string中第一次出现的位置 s::::
find

(
size_t gyjcharstring,)constfor c( size_t pos= ;
{
	< ;size_t i ++ pos) i if _size( i==[
	{
		] )c return _str;i}}
		{
			return i-
		1
	;
	} // 返回子串s在string中第一次出现的位置::::
find
(
size_t gyjconststringchar*,) constassert s( size_t pos) ;
{
	assert(s<)
	;constpos char _size*=
	+ ;while src ( _str * pos)
	const char*src=
	{
		; constchar match * s=
		; while( cur * src&&
		* ==*match ) ++match ; ++cur;
		{
			}matchif
			(cur*
		==
		')' return-match ; }else
		{
			++ src ; _str}
		}
		return
		{
			-src1
		;
	}
	// 在pos位置上插入字符c/字符串str,并返回该字符的位置 ::&::
::
insert
gyj(string, gyjcharstring)assert(size_t pos<= ) c;
{
	if(pos == _size)//扩容
	char *_size = _capacitynew
	{
		char
		[ *newstr 2 + 1]_capacity ; strcpy ( ,);
		delete[newstr] _str;=
		;*=2 _str;
		_str } newstr//移数据
		_capacity for (int
	=
	;
	( int) i ; _size-- i >= )[+pos1 ]i=
	{
		_str[i ] ;} [ _str]i=;

	++
	_str;posreturn * cthis
	_size;}

	:: &::::

insert
gyj(string, gyjconststringchar*)size_t pos= strlen () str;
{
	size_t len if (+str)//扩容
	//扩容 char_size * len > _capacity=new
	{
		char
		[ *newstr 2 + 1]_capacity ; strcpy ( ,);
		delete[newstr] _str;=
		;*=2 _str;
		_str } newstr//后移数据
		_capacity for (int
	=

	;
	( int) i ; _size-- i >= )[+pos] =i[
	{
		_str]len ; i} //拷贝字符串 _strwhilei(*
	!=

	')'
	[ ++]str = *++
	{
		_str;pos}+= ; returnstr*this
	;
	_size } len// 删除pos位置上的元素,并返回该元素的下一个位置
	:: &::::
erase

(
gyj,string) gyjassertstring(<)size_t pos; size_t lenif
{
	(+pos ) _size//pos位置之后全为0[

	] =pos ';' len >= _size=;
	{
		_str}poselse strcpy (+
		_size , pos+
	+
	)
	{
		;-=_str ; pos} _str return pos * lenthis;
		_size } lenvoid
	stringTest1
	( )::s1

(


"hello" );::
{
	gyjs2string ();::;
	gyj=string ;::s1<<.
	gyjsizestring s3(
	s3 ) s2<<

	std::cout ;s1::<<.capacity ( std)endl<<
	std::cout ; s1::<<.empty ( std)endl<<
	std::cout ; s1.resize(3 ) std;endl.

	s1resize(10);.
	s2resize(15,'A')
	s3;.reserve(4 );.

	s1reserve(20);.
	s2reserve(100);::
	s3<<[0]<<::

	std;cout :: s1<<[3 ] std<<endl::
	std;cout const s2::s4( "aaaaaa" std)endl;
	char gyj=string [3];//_CrtDumpMemoryLeaks();
	} ch void s4stringTest2()::

	s1
(

"Hello" );.
{
	gyjpush_backstring (' ');.

	s1append("World");+=
	s1'!';+="~~~~";::
	s1 << .c_str
	s1 ( )<<
	
	std::cout ; s1::s2("12345" ) std;endl.

	gyjswapstring ();.clear
	s1();s2_CrtDumpMemoryLeaks(
	s1);}[+++]
	[+++][+++][+++][+++]

[+++]

具体代可以参考我的码云

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 1778, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

文章目录

1.浅拷贝问题解决 1.1浅拷贝存在的问题


对于浅拷贝,主要存在于拷贝构造和赋值运算符重载的过程中,下面给出一段代码结合分析

#pragma warning(disable:4996)
#include

using namespace std;

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

	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}

private:
	char* _str;
};

void StringTest()
{
	String s1("Hello");
	String s2(s1);
}

int main()
{
	StringTest();
	return 0;
}

执行时程序直接崩溃


下面分析一下原因:
上述的崩溃时由于浅拷贝导致的多次释放问题

再来看下面的代码快:

分析:
本次报错是因为执行s2 = s1时,导致内存泄漏以及多次释放的问题,具体看下面的分析

总结一下:
浅拷贝会导致①内存泄露②多次释放同一块空间
这些错误都是极其严重的,我们务必要避免!

接下来就来探讨一下如何解决这些问题:

1.2通过深拷贝的方式解决浅拷贝问题

本质:让每一个对象都拥有一份独立的资源

传统版解决方式
  1. 解决拷贝构造
  2. 解决赋值运算符重载

    下面给出解决的完整代码
#pragma warning(disable:4996)
#include

using namespace std;

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(new char[strlen(s._str)+1])
	{
		strcpy(_str, s._str);
	}

	String& operator=(const String& s)
	{
		if (this != &s)
		{
			char* temp = new char[strlen(s._str) + 1];
			strcpy(temp, s._str);
			delete[] _str;
			_str = temp;
		}
		return *this;
	}

	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}

private:
	char* _str;
};

void StringTest()
{
	String s1("Hello");
	String s2("World");
	s2 = s1;

	//String s2(s1);
}

int main()
{
	StringTest();
	return 0;
}
现代版解决方式

仔细观察上面的代码,我们发现其实重复的 *** 作很多,比如每次申请新空间,拷贝元素。



现代版的方式就是采用巧妙地代码复用,将繁琐的 *** 作简洁化
具体修改在拷贝构造和赋值运算符重载处

下面我们一一解决

  1. 拷贝构造的优化
  2. 赋值运算符重载的优化

    以上便是对之前代码的一些优化!
1.3通过写时拷贝解决 理解写时拷贝方法

写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。



引用计数:用来记录资源使用者的个数。



在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,
当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,
如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;
否则就不能释放,因为还有其他对象在使用该资源。



下面使用图解的方式解释一下:

OK,看了上面的解法,有没有感觉有点问题呢?
上面的过程并没有涉及到对象内容修改,假设我现在想将上面s2对象的内容修改为“World”;由于s1 s2共用一块内存空间,所以修改s2的同时s1的内容也被修改了
但是很明显,这不是我的本意,也不符合规矩。


那如何解决这个棘手问题呢?

其实上面的这个过程还没有涉及到我们的写时拷贝。



所谓的写时拷贝是在上面的基础上解决修改(写)内容的时候发生的问题
具体步骤如下:
假设我们现在已经处于上面图示的场景,即s1和s2共用同一块内存空间,计数器此时为2
现在我想要将s2的内容改为“World”,需要执行以下步骤:
(1)为s2对象开辟新的空间
(2)将原来的空间的计数器值减减
(3)将s1对象的内容拷贝至为s2新开辟的空间中,并将s2中计数器值设置为1
(4)s2在新空间内进行修改 *** 作
下面通过图示的方式再次演示该过程:

以上便是写时拷贝的粗略思路,具体细节内容等到后续总结,目前处于扫盲状态。


1.4验证不同平台的string类是通过什么方式解决浅拷贝的

验证思路:
(1)实例化一个对象s1,长度大于15;
注意:长度必须大于15,否则无法测试出正确的结果。


因为在Windows下,string类中维护着一个空间为16的字符数组,因此小于等于15的字符串直接被存储在数组内,并不会申请空间,也就无法验证。



(2)通过拷贝构造实例化对象s2
(3)打印s1和s2对象的地址,观察地址值是否一样
如果一样 -----> 写时拷贝
如果不一样 ----> 深拷贝
验证代码如下:

void TestCopy()
{
	string s1(20, 'A');
	string s2(s1);

	printf("&s1 is %p\n",s1.c_str());
	printf("&s2 is %p\n", s2.c_str());
}

int main()
{
	TestCopy();
	return 0;
}

1、在Windows平台下的VS2013环境中

结论:vs2013中string是按照深拷贝实现的
2、在Linux平台下

结论:Linux中string是按照写时拷贝实现的

2、string类实现

有了前面的知识做铺垫,这里直接给出代码,模拟实现只是对主要的方法进行模拟,并不是完全实现一个string容器

//mystring.h
#pragma once

#include

namespace gyj
{
	class string
	{
		friend std::ostream& operator<<(std::ostream& _cout, const gyj::string& s);
	public:
		typedef char* iterator;
	public:
		/构造和析构///
		string(const char* s = "");
		string(const string& str);
		string& operator=(const string& str);
		~string();
		///迭代器相关/
		iterator begin();
		iterator end();
		///容量相关//
		size_t size()const;
		size_t capacity()const;
		bool empty()const;
		void resize(size_t n, char c = ')';void
		reserve ()size_t n;///元素访问相关//

		char
		&operator []()size_t index;const
		char &operator []()size_t indexconst;///修改相关的///
		void
		push_back (char) c;&
		stringoperator +=(char) c;void
		append (constchar *) str;&
		stringoperator +=(constchar *) str;void
		clear ();void
		swap (&string) s;const
		char *c_str ()const;/其他//
		bool
		operator <(const& string) s;bool
		operator <=(const& string) s;bool
		operator (>const& string) s;bool
		operator (>=const& string) s;bool
		operator ==(const& string) s;bool
		operator !=(const& string) s;// 返回c在string中第一次出现的位置


		find
		size_t (char, c= size_t pos 0 )const ;// 返回子串s在string中第一次出现的位置
		find
		size_t (constchar *, s= size_t pos 0 )const ;// 在pos位置上插入字符c/字符串str,并返回该字符的位置
		&
		stringinsert (,size_t poschar ) c;&
		stringinsert (,size_t posconst char *) str;// 删除pos位置上的元素,并返回该元素的下一个位置


		&
		stringerase (,size_t pos) size_t len;private
	:char
		*; _str;
		size_t _size;
		size_t _capacity}
	;}

extern
void stringTest1 ();extern
void stringTest2 ();#

接下来是mystring.cpp

pragmawarning (:disable4996)#
include#
include"mystring.h"#
include::

gyj::stringstring(constchar *) sif
{
	( nullptr== ) sassert
	{
		(0);}
	=
	_size strlen ()s;=
	_capacity ; _size=
	_str new char [+_capacity1 ];strcpy
	(,_str) s;}
::
gyj::stringstring(const& string) str:
_str(nullptr),
_size (0),
_capacity (0)temp
{
	string (.str)_str;::
	stdswap(,_str. temp)_str;=
	_size . str;_size=
	_capacity . str;_capacity}
::

gyj&string:: gyj::stringoperator=(const& string) strif
{
	( this!= & )strtemp
	{
		string (.str)_str;::
		stdswap(,_str. temp)_str;=
		_size . str;_size=
		_capacity . str;_capacity}
	return
	* this;}
::

gyjstring::~string()if
{
	( )_strdelete
	{
		[]; _str=
		_str nullptr ;}
	}
///迭代器相关/
::
gyj::string::iterator gyj::stringbegin()return
{
	; _str}
::

gyj::string::iterator gyj::stringend()return
{
	+ _str ; _size}
///容量相关//

::
size_t gyj::stringsize()constreturn
{
	; _size}
::
size_t gyj::stringcapacity()constreturn
{
	; _capacity}
bool

:: gyj::stringempty()constif
{
	( 0== ) _sizereturn
	{
		true ;}
	return
	false ;}
void
:: gyj::stringresize(,size_t nchar ) cif
{
	( )n > _sizeif
	{
		( )n > _capacityreserve
		{
			()n;}
		memset
		(+_str , _size, c- n ) _size;}
	=
	_size ; n[
	_str]_size= ';' }void
::
:: gyjreservestring()ifsize_t n(
{
	) charn > _capacity*
	{
		=new temp char [ +1n ] ;strcpy(
		,)temp; _strdelete[
		];= _str;
		_str = temp;
		_capacity } n}
	///元素访问相关//
char
&
:::: gyjoperatorstring[]()assertsize_t index(
{
	<)index ; _sizereturn[
	] _str;index}const
char
& :::: gyjoperatorstring[]()constsize_t indexassert(
{
	<)index ; _sizereturn[
	] _str;index}///修改相关的///
void
::
:: gyjpush_backstring(char)if c(
{
	== )_size //扩容 _capacityreserve
	{
		(
		*2_capacity ) ;}[
	++
	_str]_size=; [ c]
	_str=_size';' } ::&
::
gyj::stringoperator gyj+=string(char)push_back( c)
{
	;returnc*this
	; }void::
::
append gyj(stringconstchar*) if( str+
{
	strlen (_size))//扩容str= >= _capacity*
	{
		2
		size_t newcapacity + _capacity strlen ( > _size ) ?*str2 : _capacity + strlen ( _size ) +1str; reserve ()
		;}newcapacitystrcat(
	,
	);_str+= strstrlen(
	_size ) ;}str::&
::
gyj::stringoperator gyj+=string(constchar*) append( str)
{
	;returnstr*this
	; }void::
::
clear gyj(string)[0]
{
	_str=';'= 0 ;}
	_size void ::::
swap
( gyj&string)::swapstring( s,
{
	std.);_str:: sswap_str(,
	std.);_size:: sswap_size(,
	std.);_capacity} sconst_capacitychar*
::
:: c_str( gyj)stringconstreturn;}// <<重载
{
	:: _str&
::




operator
std<<ostream( gyj::&,conststd::ostream& _cout) << gyj.string; sreturn
{
	_cout ; s}_str/其他//

	bool _cout::
::

operator
< gyj(stringconst&)int= stringstrcmp s(
{
	, ret . );_strif s(_strret<
	0 )return true ;}
	{
		return false;
	}
	bool ::::
operator
<= gyj(stringconst&)return! string( s*
{
	this );}bool>s::::
operator
( gyjconststring&)>int= stringstrcmp s(
{
	, ret . );_strif s(_str0)
	return trueret > ;}
	{
		return false;
	}
	bool ::::
operator
( gyjconststring&)>=return! string( s*
{
	this <);} bool s::::
operator
== gyj(stringconst&)int= stringstrcmp s(
{
	, ret . );_strif s(_str==0
	) returnret true ;}
	{
		return false;
	}
	bool ::::
operator
!= gyj(stringconst&)return! string( s*
{
	this ==);} // 返回c在string中第一次出现的位置 s::::
find

(
size_t gyjcharstring,)constfor c( size_t pos= ;
{
	< ;size_t i ++ pos) i if _size( i==[
	{
		] )c return _str;i}}
		{
			return i-
		1
	;
	} // 返回子串s在string中第一次出现的位置::::
find
(
size_t gyjconststringchar*,) constassert s( size_t pos) ;
{
	assert(s<)
	;constpos char _size*=
	+ ;while src ( _str * pos)
	const char*src=
	{
		; constchar match * s=
		; while( cur * src&&
		* ==*match ) ++match ; ++cur;
		{
			}matchif
			(cur*
		==
		')' return-match ; }else
		{
			++ src ; _str}
		}
		return
		{
			-src1
		;
	}
	// 在pos位置上插入字符c/字符串str,并返回该字符的位置 ::&::
::
insert
gyj(string, gyjcharstring)assert(size_t pos<= ) c;
{
	if(pos == _size)//扩容
	char *_size = _capacitynew
	{
		char
		[ *newstr 2 + 1]_capacity ; strcpy ( ,);
		delete[newstr] _str;=
		;*=2 _str;
		_str } newstr//移数据
		_capacity for (int
	=
	;
	( int) i ; _size-- i >= )[+pos1 ]i=
	{
		_str[i ] ;} [ _str]i=;

	++
	_str;posreturn * cthis
	_size;}

	:: &::::

insert
gyj(string, gyjconststringchar*)size_t pos= strlen () str;
{
	size_t len if (+str)//扩容
	//扩容 char_size * len > _capacity=new
	{
		char
		[ *newstr 2 + 1]_capacity ; strcpy ( ,);
		delete[newstr] _str;=
		;*=2 _str;
		_str } newstr//后移数据
		_capacity for (int
	=

	;
	( int) i ; _size-- i >= )[+pos] =i[
	{
		_str]len ; i} //拷贝字符串 _strwhilei(*
	!=

	')'
	[ ++]str = *++
	{
		_str;pos}+= ; returnstr*this
	;
	_size } len// 删除pos位置上的元素,并返回该元素的下一个位置
	:: &::::
erase

(
gyj,string) gyjassertstring(<)size_t pos; size_t lenif
{
	(+pos ) _size//pos位置之后全为0[

	] =pos ';' len >= _size=;
	{
		_str}poselse strcpy (+
		_size , pos+
	+
	)
	{
		;-=_str ; pos} _str return pos * lenthis;
		_size } lenvoid
	stringTest1
	( )::s1

(


"hello" );::
{
	gyjs2string ();::;
	gyj=string ;::s1<<.
	gyjsizestring s3(
	s3 ) s2<<

	std::cout ;s1::<<.capacity ( std)endl<<
	std::cout ; s1::<<.empty ( std)endl<<
	std::cout ; s1.resize(3 ) std;endl.

	s1resize(10);.
	s2resize(15,'A')
	s3;.reserve(4 );.

	s1reserve(20);.
	s2reserve(100);::
	s3<<[0]<<::

	std;cout :: s1<<[3 ] std<<endl::
	std;cout const s2::s4( "aaaaaa" std)endl;
	char gyj=string [3];//_CrtDumpMemoryLeaks();
	} ch void s4stringTest2()::

	s1
(

"Hello" );.
{
	gyjpush_backstring (' ');.

	s1append("World");+=
	s1'!';+="~~~~";::
	s1 << .c_str
	s1 ( )<<
	
	std::cout ; s1::s2("12345" ) std;endl.

	gyjswapstring ();.clear
	s1();s2_CrtDumpMemoryLeaks(
	s1);}
	[+++][+++][+++][+++]

[+++]

具体代可以参考我的码云

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 1779, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

文章目录

1.浅拷贝问题解决 1.1浅拷贝存在的问题


对于浅拷贝,主要存在于拷贝构造和赋值运算符重载的过程中,下面给出一段代码结合分析

#pragma warning(disable:4996)
#include

using namespace std;

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

	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}

private:
	char* _str;
};

void StringTest()
{
	String s1("Hello");
	String s2(s1);
}

int main()
{
	StringTest();
	return 0;
}

执行时程序直接崩溃


下面分析一下原因:
上述的崩溃时由于浅拷贝导致的多次释放问题

再来看下面的代码快:

分析:
本次报错是因为执行s2 = s1时,导致内存泄漏以及多次释放的问题,具体看下面的分析

总结一下:
浅拷贝会导致①内存泄露②多次释放同一块空间
这些错误都是极其严重的,我们务必要避免!

接下来就来探讨一下如何解决这些问题:

1.2通过深拷贝的方式解决浅拷贝问题

本质:让每一个对象都拥有一份独立的资源

传统版解决方式
  1. 解决拷贝构造
  2. 解决赋值运算符重载

    下面给出解决的完整代码
#pragma warning(disable:4996)
#include

using namespace std;

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(new char[strlen(s._str)+1])
	{
		strcpy(_str, s._str);
	}

	String& operator=(const String& s)
	{
		if (this != &s)
		{
			char* temp = new char[strlen(s._str) + 1];
			strcpy(temp, s._str);
			delete[] _str;
			_str = temp;
		}
		return *this;
	}

	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}

private:
	char* _str;
};

void StringTest()
{
	String s1("Hello");
	String s2("World");
	s2 = s1;

	//String s2(s1);
}

int main()
{
	StringTest();
	return 0;
}
现代版解决方式

仔细观察上面的代码,我们发现其实重复的 *** 作很多,比如每次申请新空间,拷贝元素。



现代版的方式就是采用巧妙地代码复用,将繁琐的 *** 作简洁化
具体修改在拷贝构造和赋值运算符重载处

下面我们一一解决

  1. 拷贝构造的优化
  2. 赋值运算符重载的优化

    以上便是对之前代码的一些优化!
1.3通过写时拷贝解决 理解写时拷贝方法

写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。



引用计数:用来记录资源使用者的个数。



在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,
当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,
如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;
否则就不能释放,因为还有其他对象在使用该资源。



下面使用图解的方式解释一下:

OK,看了上面的解法,有没有感觉有点问题呢?
上面的过程并没有涉及到对象内容修改,假设我现在想将上面s2对象的内容修改为“World”;由于s1 s2共用一块内存空间,所以修改s2的同时s1的内容也被修改了
但是很明显,这不是我的本意,也不符合规矩。


那如何解决这个棘手问题呢?

其实上面的这个过程还没有涉及到我们的写时拷贝。



所谓的写时拷贝是在上面的基础上解决修改(写)内容的时候发生的问题
具体步骤如下:
假设我们现在已经处于上面图示的场景,即s1和s2共用同一块内存空间,计数器此时为2
现在我想要将s2的内容改为“World”,需要执行以下步骤:
(1)为s2对象开辟新的空间
(2)将原来的空间的计数器值减减
(3)将s1对象的内容拷贝至为s2新开辟的空间中,并将s2中计数器值设置为1
(4)s2在新空间内进行修改 *** 作
下面通过图示的方式再次演示该过程:

以上便是写时拷贝的粗略思路,具体细节内容等到后续总结,目前处于扫盲状态。


1.4验证不同平台的string类是通过什么方式解决浅拷贝的

验证思路:
(1)实例化一个对象s1,长度大于15;
注意:长度必须大于15,否则无法测试出正确的结果。


因为在Windows下,string类中维护着一个空间为16的字符数组,因此小于等于15的字符串直接被存储在数组内,并不会申请空间,也就无法验证。



(2)通过拷贝构造实例化对象s2
(3)打印s1和s2对象的地址,观察地址值是否一样
如果一样 -----> 写时拷贝
如果不一样 ----> 深拷贝
验证代码如下:

void TestCopy()
{
	string s1(20, 'A');
	string s2(s1);

	printf("&s1 is %p\n",s1.c_str());
	printf("&s2 is %p\n", s2.c_str());
}

int main()
{
	TestCopy();
	return 0;
}

1、在Windows平台下的VS2013环境中

结论:vs2013中string是按照深拷贝实现的
2、在Linux平台下

结论:Linux中string是按照写时拷贝实现的

2、string类实现

有了前面的知识做铺垫,这里直接给出代码,模拟实现只是对主要的方法进行模拟,并不是完全实现一个string容器

//mystring.h
#pragma once

#include

namespace gyj
{
	class string
	{
		friend std::ostream& operator<<(std::ostream& _cout, const gyj::string& s);
	public:
		typedef char* iterator;
	public:
		/构造和析构///
		string(const char* s = "");
		string(const string& str);
		string& operator=(const string& str);
		~string();
		///迭代器相关/
		iterator begin();
		iterator end();
		///容量相关//
		size_t size()const;
		size_t capacity()const;
		bool empty()const;
		void resize(size_t n, char c = ')';void
		reserve ()size_t n;///元素访问相关//

		char
		&operator []()size_t index;const
		char &operator []()size_t indexconst;///修改相关的///
		void
		push_back (char) c;&
		stringoperator +=(char) c;void
		append (constchar *) str;&
		stringoperator +=(constchar *) str;void
		clear ();void
		swap (&string) s;const
		char *c_str ()const;/其他//
		bool
		operator <(const& string) s;bool
		operator <=(const& string) s;bool
		operator (>const& string) s;bool
		operator (>=const& string) s;bool
		operator ==(const& string) s;bool
		operator !=(const& string) s;// 返回c在string中第一次出现的位置


		find
		size_t (char, c= size_t pos 0 )const ;// 返回子串s在string中第一次出现的位置
		find
		size_t (constchar *, s= size_t pos 0 )const ;// 在pos位置上插入字符c/字符串str,并返回该字符的位置
		&
		stringinsert (,size_t poschar ) c;&
		stringinsert (,size_t posconst char *) str;// 删除pos位置上的元素,并返回该元素的下一个位置


		&
		stringerase (,size_t pos) size_t len;private
	:char
		*; _str;
		size_t _size;
		size_t _capacity}
	;}

extern
void stringTest1 ();extern
void stringTest2 ();#

接下来是mystring.cpp

pragmawarning (:disable4996)#
include#
include"mystring.h"#
include::

gyj::stringstring(constchar *) sif
{
	( nullptr== ) sassert
	{
		(0);}
	=
	_size strlen ()s;=
	_capacity ; _size=
	_str new char [+_capacity1 ];strcpy
	(,_str) s;}
::
gyj::stringstring(const& string) str:
_str(nullptr),
_size (0),
_capacity (0)temp
{
	string (.str)_str;::
	stdswap(,_str. temp)_str;=
	_size . str;_size=
	_capacity . str;_capacity}
::

gyj&string:: gyj::stringoperator=(const& string) strif
{
	( this!= & )strtemp
	{
		string (.str)_str;::
		stdswap(,_str. temp)_str;=
		_size . str;_size=
		_capacity . str;_capacity}
	return
	* this;}
::

gyjstring::~string()if
{
	( )_strdelete
	{
		[]; _str=
		_str nullptr ;}
	}
///迭代器相关/
::
gyj::string::iterator gyj::stringbegin()return
{
	; _str}
::

gyj::string::iterator gyj::stringend()return
{
	+ _str ; _size}
///容量相关//

::
size_t gyj::stringsize()constreturn
{
	; _size}
::
size_t gyj::stringcapacity()constreturn
{
	; _capacity}
bool

:: gyj::stringempty()constif
{
	( 0== ) _sizereturn
	{
		true ;}
	return
	false ;}
void
:: gyj::stringresize(,size_t nchar ) cif
{
	( )n > _sizeif
	{
		( )n > _capacityreserve
		{
			()n;}
		memset
		(+_str , _size, c- n ) _size;}
	=
	_size ; n[
	_str]_size= ';' }void
::
:: gyjreservestring()ifsize_t n(
{
	) charn > _capacity*
	{
		=new temp char [ +1n ] ;strcpy(
		,)temp; _strdelete[
		];= _str;
		_str = temp;
		_capacity } n}
	///元素访问相关//
char
&
:::: gyjoperatorstring[]()assertsize_t index(
{
	<)index ; _sizereturn[
	] _str;index}const
char
& :::: gyjoperatorstring[]()constsize_t indexassert(
{
	<)index ; _sizereturn[
	] _str;index}///修改相关的///
void
::
:: gyjpush_backstring(char)if c(
{
	== )_size //扩容 _capacityreserve
	{
		(
		*2_capacity ) ;}[
	++
	_str]_size=; [ c]
	_str=_size';' } ::&
::
gyj::stringoperator gyj+=string(char)push_back( c)
{
	;returnc*this
	; }void::
::
append gyj(stringconstchar*) if( str+
{
	strlen (_size))//扩容str= >= _capacity*
	{
		2
		size_t newcapacity + _capacity strlen ( > _size ) ?*str2 : _capacity + strlen ( _size ) +1str; reserve ()
		;}newcapacitystrcat(
	,
	);_str+= strstrlen(
	_size ) ;}str::&
::
gyj::stringoperator gyj+=string(constchar*) append( str)
{
	;returnstr*this
	; }void::
::
clear gyj(string)[0]
{
	_str=';'= 0 ;}
	_size void ::::
swap
( gyj&string)::swapstring( s,
{
	std.);_str:: sswap_str(,
	std.);_size:: sswap_size(,
	std.);_capacity} sconst_capacitychar*
::
:: c_str( gyj)stringconstreturn;}// <<重载
{
	:: _str&
::




operator
std<<ostream( gyj::&,conststd::ostream& _cout) << gyj.string; sreturn
{
	_cout ; s}_str/其他//

	bool _cout::
::

operator
< gyj(stringconst&)int= stringstrcmp s(
{
	, ret . );_strif s(_strret<
	0 )return true ;}
	{
		return false;
	}
	bool ::::
operator
<= gyj(stringconst&)return! string( s*
{
	this );}bool>s::::
operator
( gyjconststring&)>int= stringstrcmp s(
{
	, ret . );_strif s(_str0)
	return trueret > ;}
	{
		return false;
	}
	bool ::::
operator
( gyjconststring&)>=return! string( s*
{
	this <);} bool s::::
operator
== gyj(stringconst&)int= stringstrcmp s(
{
	, ret . );_strif s(_str==0
	) returnret true ;}
	{
		return false;
	}
	bool ::::
operator
!= gyj(stringconst&)return! string( s*
{
	this ==);} // 返回c在string中第一次出现的位置 s::::
find

(
size_t gyjcharstring,)constfor c( size_t pos= ;
{
	< ;size_t i ++ pos) i if _size( i==[
	{
		] )c return _str;i}}
		{
			return i-
		1
	;
	} // 返回子串s在string中第一次出现的位置::::
find
(
size_t gyjconststringchar*,) constassert s( size_t pos) ;
{
	assert(s<)
	;constpos char _size*=
	+ ;while src ( _str * pos)
	const char*src=
	{
		; constchar match * s=
		; while( cur * src&&
		* ==*match ) ++match ; ++cur;
		{
			}matchif
			(cur*
		==
		')' return-match ; }else
		{
			++ src ; _str}
		}
		return
		{
			-src1
		;
	}
	// 在pos位置上插入字符c/字符串str,并返回该字符的位置 ::&::
::
insert
gyj(string, gyjcharstring)assert(size_t pos<= ) c;
{
	if(pos == _size)//扩容
	char *_size = _capacitynew
	{
		char
		[ *newstr 2 + 1]_capacity ; strcpy ( ,);
		delete[newstr] _str;=
		;*=2 _str;
		_str } newstr//移数据
		_capacity for (int
	=
	;
	( int) i ; _size-- i >= )[+pos1 ]i=
	{
		_str[i ] ;} [ _str]i=;

	++
	_str;posreturn * cthis
	_size;}

	:: &::::

insert
gyj(string, gyjconststringchar*)size_t pos= strlen () str;
{
	size_t len if (+str)//扩容
	//扩容 char_size * len > _capacity=new
	{
		char
		[ *newstr 2 + 1]_capacity ; strcpy ( ,);
		delete[newstr] _str;=
		;*=2 _str;
		_str } newstr//后移数据
		_capacity for (int
	=

	;
	( int) i ; _size-- i >= )[+pos] =i[
	{
		_str]len ; i} //拷贝字符串 _strwhilei(*
	!=

	')'
	[ ++]str = *++
	{
		_str;pos}+= ; returnstr*this
	;
	_size } len// 删除pos位置上的元素,并返回该元素的下一个位置
	:: &::::
erase

(
gyj,string) gyjassertstring(<)size_t pos; size_t lenif
{
	(+pos ) _size//pos位置之后全为0[

	] =pos ';' len >= _size=;
	{
		_str}poselse strcpy (+
		_size , pos+
	+
	)
	{
		;-=_str ; pos} _str return pos * lenthis;
		_size } lenvoid
	stringTest1
	( )::s1

(


"hello" );::
{
	gyjs2string ();::;
	gyj=string ;::s1<<.
	gyjsizestring s3(
	s3 ) s2<<

	std::cout ;s1::<<.capacity ( std)endl<<
	std::cout ; s1::<<.empty ( std)endl<<
	std::cout ; s1.resize(3 ) std;endl.

	s1resize(10);.
	s2resize(15,'A')
	s3;.reserve(4 );.

	s1reserve(20);.
	s2reserve(100);::
	s3<<[0]<<::

	std;cout :: s1<<[3 ] std<<endl::
	std;cout const s2::s4( "aaaaaa" std)endl;
	char gyj=string [3];//_CrtDumpMemoryLeaks();
	} ch void s4stringTest2()::

	s1
(

"Hello" );.
{
	gyjpush_backstring (' ');.

	s1append("World");+=
	s1'!';+="~~~~";::
	s1 << .c_str
	s1 ( )<<
	
	std::cout ; s1::s2("12345" ) std;endl.

	gyjswapstring ();.clear
	s1();s2_CrtDumpMemoryLeaks(
	s1);}
	[+++][+++][+++]

[+++]

具体代可以参考我的码云

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 1780, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

文章目录

1.浅拷贝问题解决 1.1浅拷贝存在的问题


对于浅拷贝,主要存在于拷贝构造和赋值运算符重载的过程中,下面给出一段代码结合分析

#pragma warning(disable:4996)
#include

using namespace std;

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

	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}

private:
	char* _str;
};

void StringTest()
{
	String s1("Hello");
	String s2(s1);
}

int main()
{
	StringTest();
	return 0;
}

执行时程序直接崩溃


下面分析一下原因:
上述的崩溃时由于浅拷贝导致的多次释放问题

再来看下面的代码快:

分析:
本次报错是因为执行s2 = s1时,导致内存泄漏以及多次释放的问题,具体看下面的分析

总结一下:
浅拷贝会导致①内存泄露②多次释放同一块空间
这些错误都是极其严重的,我们务必要避免!

接下来就来探讨一下如何解决这些问题:

1.2通过深拷贝的方式解决浅拷贝问题

本质:让每一个对象都拥有一份独立的资源

传统版解决方式
  1. 解决拷贝构造
  2. 解决赋值运算符重载

    下面给出解决的完整代码
#pragma warning(disable:4996)
#include

using namespace std;

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(new char[strlen(s._str)+1])
	{
		strcpy(_str, s._str);
	}

	String& operator=(const String& s)
	{
		if (this != &s)
		{
			char* temp = new char[strlen(s._str) + 1];
			strcpy(temp, s._str);
			delete[] _str;
			_str = temp;
		}
		return *this;
	}

	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}

private:
	char* _str;
};

void StringTest()
{
	String s1("Hello");
	String s2("World");
	s2 = s1;

	//String s2(s1);
}

int main()
{
	StringTest();
	return 0;
}
现代版解决方式

仔细观察上面的代码,我们发现其实重复的 *** 作很多,比如每次申请新空间,拷贝元素。



现代版的方式就是采用巧妙地代码复用,将繁琐的 *** 作简洁化
具体修改在拷贝构造和赋值运算符重载处

下面我们一一解决

  1. 拷贝构造的优化
  2. 赋值运算符重载的优化

    以上便是对之前代码的一些优化!
1.3通过写时拷贝解决 理解写时拷贝方法

写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。



引用计数:用来记录资源使用者的个数。



在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,
当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,
如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;
否则就不能释放,因为还有其他对象在使用该资源。



下面使用图解的方式解释一下:

OK,看了上面的解法,有没有感觉有点问题呢?
上面的过程并没有涉及到对象内容修改,假设我现在想将上面s2对象的内容修改为“World”;由于s1 s2共用一块内存空间,所以修改s2的同时s1的内容也被修改了
但是很明显,这不是我的本意,也不符合规矩。


那如何解决这个棘手问题呢?

其实上面的这个过程还没有涉及到我们的写时拷贝。



所谓的写时拷贝是在上面的基础上解决修改(写)内容的时候发生的问题
具体步骤如下:
假设我们现在已经处于上面图示的场景,即s1和s2共用同一块内存空间,计数器此时为2
现在我想要将s2的内容改为“World”,需要执行以下步骤:
(1)为s2对象开辟新的空间
(2)将原来的空间的计数器值减减
(3)将s1对象的内容拷贝至为s2新开辟的空间中,并将s2中计数器值设置为1
(4)s2在新空间内进行修改 *** 作
下面通过图示的方式再次演示该过程:

以上便是写时拷贝的粗略思路,具体细节内容等到后续总结,目前处于扫盲状态。


1.4验证不同平台的string类是通过什么方式解决浅拷贝的

验证思路:
(1)实例化一个对象s1,长度大于15;
注意:长度必须大于15,否则无法测试出正确的结果。


因为在Windows下,string类中维护着一个空间为16的字符数组,因此小于等于15的字符串直接被存储在数组内,并不会申请空间,也就无法验证。



(2)通过拷贝构造实例化对象s2
(3)打印s1和s2对象的地址,观察地址值是否一样
如果一样 -----> 写时拷贝
如果不一样 ----> 深拷贝
验证代码如下:

void TestCopy()
{
	string s1(20, 'A');
	string s2(s1);

	printf("&s1 is %p\n",s1.c_str());
	printf("&s2 is %p\n", s2.c_str());
}

int main()
{
	TestCopy();
	return 0;
}

1、在Windows平台下的VS2013环境中

结论:vs2013中string是按照深拷贝实现的
2、在Linux平台下

结论:Linux中string是按照写时拷贝实现的

2、string类实现

有了前面的知识做铺垫,这里直接给出代码,模拟实现只是对主要的方法进行模拟,并不是完全实现一个string容器

//mystring.h
#pragma once

#include

namespace gyj
{
	class string
	{
		friend std::ostream& operator<<(std::ostream& _cout, const gyj::string& s);
	public:
		typedef char* iterator;
	public:
		/构造和析构///
		string(const char* s = "");
		string(const string& str);
		string& operator=(const string& str);
		~string();
		///迭代器相关/
		iterator begin();
		iterator end();
		///容量相关//
		size_t size()const;
		size_t capacity()const;
		bool empty()const;
		void resize(size_t n, char c = ')';void
		reserve ()size_t n;///元素访问相关//

		char
		&operator []()size_t index;const
		char &operator []()size_t indexconst;///修改相关的///
		void
		push_back (char) c;&
		stringoperator +=(char) c;void
		append (constchar *) str;&
		stringoperator +=(constchar *) str;void
		clear ();void
		swap (&string) s;const
		char *c_str ()const;/其他//
		bool
		operator <(const& string) s;bool
		operator <=(const& string) s;bool
		operator (>const& string) s;bool
		operator (>=const& string) s;bool
		operator ==(const& string) s;bool
		operator !=(const& string) s;// 返回c在string中第一次出现的位置


		find
		size_t (char, c= size_t pos 0 )const ;// 返回子串s在string中第一次出现的位置
		find
		size_t (constchar *, s= size_t pos 0 )const ;// 在pos位置上插入字符c/字符串str,并返回该字符的位置
		&
		stringinsert (,size_t poschar ) c;&
		stringinsert (,size_t posconst char *) str;// 删除pos位置上的元素,并返回该元素的下一个位置


		&
		stringerase (,size_t pos) size_t len;private
	:char
		*; _str;
		size_t _size;
		size_t _capacity}
	;}

extern
void stringTest1 ();extern
void stringTest2 ();#

接下来是mystring.cpp

pragmawarning (:disable4996)#
include#
include"mystring.h"#
include::

gyj::stringstring(constchar *) sif
{
	( nullptr== ) sassert
	{
		(0);}
	=
	_size strlen ()s;=
	_capacity ; _size=
	_str new char [+_capacity1 ];strcpy
	(,_str) s;}
::
gyj::stringstring(const& string) str:
_str(nullptr),
_size (0),
_capacity (0)temp
{
	string (.str)_str;::
	stdswap(,_str. temp)_str;=
	_size . str;_size=
	_capacity . str;_capacity}
::

gyj&string:: gyj::stringoperator=(const& string) strif
{
	( this!= & )strtemp
	{
		string (.str)_str;::
		stdswap(,_str. temp)_str;=
		_size . str;_size=
		_capacity . str;_capacity}
	return
	* this;}
::

gyjstring::~string()if
{
	( )_strdelete
	{
		[]; _str=
		_str nullptr ;}
	}
///迭代器相关/
::
gyj::string::iterator gyj::stringbegin()return
{
	; _str}
::

gyj::string::iterator gyj::stringend()return
{
	+ _str ; _size}
///容量相关//

::
size_t gyj::stringsize()constreturn
{
	; _size}
::
size_t gyj::stringcapacity()constreturn
{
	; _capacity}
bool

:: gyj::stringempty()constif
{
	( 0== ) _sizereturn
	{
		true ;}
	return
	false ;}
void
:: gyj::stringresize(,size_t nchar ) cif
{
	( )n > _sizeif
	{
		( )n > _capacityreserve
		{
			()n;}
		memset
		(+_str , _size, c- n ) _size;}
	=
	_size ; n[
	_str]_size= ';' }void
::
:: gyjreservestring()ifsize_t n(
{
	) charn > _capacity*
	{
		=new temp char [ +1n ] ;strcpy(
		,)temp; _strdelete[
		];= _str;
		_str = temp;
		_capacity } n}
	///元素访问相关//
char
&
:::: gyjoperatorstring[]()assertsize_t index(
{
	<)index ; _sizereturn[
	] _str;index}const
char
& :::: gyjoperatorstring[]()constsize_t indexassert(
{
	<)index ; _sizereturn[
	] _str;index}///修改相关的///
void
::
:: gyjpush_backstring(char)if c(
{
	== )_size //扩容 _capacityreserve
	{
		(
		*2_capacity ) ;}[
	++
	_str]_size=; [ c]
	_str=_size';' } ::&
::
gyj::stringoperator gyj+=string(char)push_back( c)
{
	;returnc*this
	; }void::
::
append gyj(stringconstchar*) if( str+
{
	strlen (_size))//扩容str= >= _capacity*
	{
		2
		size_t newcapacity + _capacity strlen ( > _size ) ?*str2 : _capacity + strlen ( _size ) +1str; reserve ()
		;}newcapacitystrcat(
	,
	);_str+= strstrlen(
	_size ) ;}str::&
::
gyj::stringoperator gyj+=string(constchar*) append( str)
{
	;returnstr*this
	; }void::
::
clear gyj(string)[0]
{
	_str=';'= 0 ;}
	_size void ::::
swap
( gyj&string)::swapstring( s,
{
	std.);_str:: sswap_str(,
	std.);_size:: sswap_size(,
	std.);_capacity} sconst_capacitychar*
::
:: c_str( gyj)stringconstreturn;}// <<重载
{
	:: _str&
::




operator
std<<ostream( gyj::&,conststd::ostream& _cout) << gyj.string; sreturn
{
	_cout ; s}_str/其他//

	bool _cout::
::

operator
< gyj(stringconst&)int= stringstrcmp s(
{
	, ret . );_strif s(_strret<
	0 )return true ;}
	{
		return false;
	}
	bool ::::
operator
<= gyj(stringconst&)return! string( s*
{
	this );}bool>s::::
operator
( gyjconststring&)>int= stringstrcmp s(
{
	, ret . );_strif s(_str0)
	return trueret > ;}
	{
		return false;
	}
	bool ::::
operator
( gyjconststring&)>=return! string( s*
{
	this <);} bool s::::
operator
== gyj(stringconst&)int= stringstrcmp s(
{
	, ret . );_strif s(_str==0
	) returnret true ;}
	{
		return false;
	}
	bool ::::
operator
!= gyj(stringconst&)return! string( s*
{
	this ==);} // 返回c在string中第一次出现的位置 s::::
find

(
size_t gyjcharstring,)constfor c( size_t pos= ;
{
	< ;size_t i ++ pos) i if _size( i==[
	{
		] )c return _str;i}}
		{
			return i-
		1
	;
	} // 返回子串s在string中第一次出现的位置::::
find
(
size_t gyjconststringchar*,) constassert s( size_t pos) ;
{
	assert(s<)
	;constpos char _size*=
	+ ;while src ( _str * pos)
	const char*src=
	{
		; constchar match * s=
		; while( cur * src&&
		* ==*match ) ++match ; ++cur;
		{
			}matchif
			(cur*
		==
		')' return-match ; }else
		{
			++ src ; _str}
		}
		return
		{
			-src1
		;
	}
	// 在pos位置上插入字符c/字符串str,并返回该字符的位置 ::&::
::
insert
gyj(string, gyjcharstring)assert(size_t pos<= ) c;
{
	if(pos == _size)//扩容
	char *_size = _capacitynew
	{
		char
		[ *newstr 2 + 1]_capacity ; strcpy ( ,);
		delete[newstr] _str;=
		;*=2 _str;
		_str } newstr//移数据
		_capacity for (int
	=
	;
	( int) i ; _size-- i >= )[+pos1 ]i=
	{
		_str[i ] ;} [ _str]i=;

	++
	_str;posreturn * cthis
	_size;}

	:: &::::

insert
gyj(string, gyjconststringchar*)size_t pos= strlen () str;
{
	size_t len if (+str)//扩容
	//扩容 char_size * len > _capacity=new
	{
		char
		[ *newstr 2 + 1]_capacity ; strcpy ( ,);
		delete[newstr] _str;=
		;*=2 _str;
		_str } newstr//后移数据
		_capacity for (int
	=

	;
	( int) i ; _size-- i >= )[+pos] =i[
	{
		_str]len ; i} //拷贝字符串 _strwhilei(*
	!=

	')'
	[ ++]str = *++
	{
		_str;pos}+= ; returnstr*this
	;
	_size } len// 删除pos位置上的元素,并返回该元素的下一个位置
	:: &::::
erase

(
gyj,string) gyjassertstring(<)size_t pos; size_t lenif
{
	(+pos ) _size//pos位置之后全为0[

	] =pos ';' len >= _size=;
	{
		_str}poselse strcpy (+
		_size , pos+
	+
	)
	{
		;-=_str ; pos} _str return pos * lenthis;
		_size } lenvoid
	stringTest1
	( )::s1

(


"hello" );::
{
	gyjs2string ();::;
	gyj=string ;::s1<<.
	gyjsizestring s3(
	s3 ) s2<<

	std::cout ;s1::<<.capacity ( std)endl<<
	std::cout ; s1::<<.empty ( std)endl<<
	std::cout ; s1.resize(3 ) std;endl.

	s1resize(10);.
	s2resize(15,'A')
	s3;.reserve(4 );.

	s1reserve(20);.
	s2reserve(100);::
	s3<<[0]<<::

	std;cout :: s1<<[3 ] std<<endl::
	std;cout const s2::s4( "aaaaaa" std)endl;
	char gyj=string [3];//_CrtDumpMemoryLeaks();
	} ch void s4stringTest2()::

	s1
(

"Hello" );.
{
	gyjpush_backstring (' ');.

	s1append("World");+=
	s1'!';+="~~~~";::
	s1 << .c_str
	s1 ( )<<
	
	std::cout ; s1::s2("12345" ) std;endl.

	gyjswapstring ();.clear
	s1();s2_CrtDumpMemoryLeaks(
	s1);}
	[+++][+++]

[+++]

具体代可以参考我的码云

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 1781, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

文章目录

1.浅拷贝问题解决 1.1浅拷贝存在的问题


对于浅拷贝,主要存在于拷贝构造和赋值运算符重载的过程中,下面给出一段代码结合分析

#pragma warning(disable:4996)
#include

using namespace std;

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

	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}

private:
	char* _str;
};

void StringTest()
{
	String s1("Hello");
	String s2(s1);
}

int main()
{
	StringTest();
	return 0;
}

执行时程序直接崩溃


下面分析一下原因:
上述的崩溃时由于浅拷贝导致的多次释放问题

再来看下面的代码快:

分析:
本次报错是因为执行s2 = s1时,导致内存泄漏以及多次释放的问题,具体看下面的分析

总结一下:
浅拷贝会导致①内存泄露②多次释放同一块空间
这些错误都是极其严重的,我们务必要避免!

接下来就来探讨一下如何解决这些问题:

1.2通过深拷贝的方式解决浅拷贝问题

本质:让每一个对象都拥有一份独立的资源

传统版解决方式
  1. 解决拷贝构造
  2. 解决赋值运算符重载

    下面给出解决的完整代码
#pragma warning(disable:4996)
#include

using namespace std;

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(new char[strlen(s._str)+1])
	{
		strcpy(_str, s._str);
	}

	String& operator=(const String& s)
	{
		if (this != &s)
		{
			char* temp = new char[strlen(s._str) + 1];
			strcpy(temp, s._str);
			delete[] _str;
			_str = temp;
		}
		return *this;
	}

	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}

private:
	char* _str;
};

void StringTest()
{
	String s1("Hello");
	String s2("World");
	s2 = s1;

	//String s2(s1);
}

int main()
{
	StringTest();
	return 0;
}
现代版解决方式

仔细观察上面的代码,我们发现其实重复的 *** 作很多,比如每次申请新空间,拷贝元素。



现代版的方式就是采用巧妙地代码复用,将繁琐的 *** 作简洁化
具体修改在拷贝构造和赋值运算符重载处

下面我们一一解决

  1. 拷贝构造的优化
  2. 赋值运算符重载的优化

    以上便是对之前代码的一些优化!
1.3通过写时拷贝解决 理解写时拷贝方法

写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。



引用计数:用来记录资源使用者的个数。



在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,
当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,
如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;
否则就不能释放,因为还有其他对象在使用该资源。



下面使用图解的方式解释一下:

OK,看了上面的解法,有没有感觉有点问题呢?
上面的过程并没有涉及到对象内容修改,假设我现在想将上面s2对象的内容修改为“World”;由于s1 s2共用一块内存空间,所以修改s2的同时s1的内容也被修改了
但是很明显,这不是我的本意,也不符合规矩。


那如何解决这个棘手问题呢?

其实上面的这个过程还没有涉及到我们的写时拷贝。



所谓的写时拷贝是在上面的基础上解决修改(写)内容的时候发生的问题
具体步骤如下:
假设我们现在已经处于上面图示的场景,即s1和s2共用同一块内存空间,计数器此时为2
现在我想要将s2的内容改为“World”,需要执行以下步骤:
(1)为s2对象开辟新的空间
(2)将原来的空间的计数器值减减
(3)将s1对象的内容拷贝至为s2新开辟的空间中,并将s2中计数器值设置为1
(4)s2在新空间内进行修改 *** 作
下面通过图示的方式再次演示该过程:

以上便是写时拷贝的粗略思路,具体细节内容等到后续总结,目前处于扫盲状态。


1.4验证不同平台的string类是通过什么方式解决浅拷贝的

验证思路:
(1)实例化一个对象s1,长度大于15;
注意:长度必须大于15,否则无法测试出正确的结果。


因为在Windows下,string类中维护着一个空间为16的字符数组,因此小于等于15的字符串直接被存储在数组内,并不会申请空间,也就无法验证。



(2)通过拷贝构造实例化对象s2
(3)打印s1和s2对象的地址,观察地址值是否一样
如果一样 -----> 写时拷贝
如果不一样 ----> 深拷贝
验证代码如下:

void TestCopy()
{
	string s1(20, 'A');
	string s2(s1);

	printf("&s1 is %p\n",s1.c_str());
	printf("&s2 is %p\n", s2.c_str());
}

int main()
{
	TestCopy();
	return 0;
}

1、在Windows平台下的VS2013环境中

结论:vs2013中string是按照深拷贝实现的
2、在Linux平台下

结论:Linux中string是按照写时拷贝实现的

2、string类实现

有了前面的知识做铺垫,这里直接给出代码,模拟实现只是对主要的方法进行模拟,并不是完全实现一个string容器

//mystring.h
#pragma once

#include

namespace gyj
{
	class string
	{
		friend std::ostream& operator<<(std::ostream& _cout, const gyj::string& s);
	public:
		typedef char* iterator;
	public:
		/构造和析构///
		string(const char* s = "");
		string(const string& str);
		string& operator=(const string& str);
		~string();
		///迭代器相关/
		iterator begin();
		iterator end();
		///容量相关//
		size_t size()const;
		size_t capacity()const;
		bool empty()const;
		void resize(size_t n, char c = ')';void
		reserve ()size_t n;///元素访问相关//

		char
		&operator []()size_t index;const
		char &operator []()size_t indexconst;///修改相关的///
		void
		push_back (char) c;&
		stringoperator +=(char) c;void
		append (constchar *) str;&
		stringoperator +=(constchar *) str;void
		clear ();void
		swap (&string) s;const
		char *c_str ()const;/其他//
		bool
		operator <(const& string) s;bool
		operator <=(const& string) s;bool
		operator (>const& string) s;bool
		operator (>=const& string) s;bool
		operator ==(const& string) s;bool
		operator !=(const& string) s;// 返回c在string中第一次出现的位置


		find
		size_t (char, c= size_t pos 0 )const ;// 返回子串s在string中第一次出现的位置
		find
		size_t (constchar *, s= size_t pos 0 )const ;// 在pos位置上插入字符c/字符串str,并返回该字符的位置
		&
		stringinsert (,size_t poschar ) c;&
		stringinsert (,size_t posconst char *) str;// 删除pos位置上的元素,并返回该元素的下一个位置


		&
		stringerase (,size_t pos) size_t len;private
	:char
		*; _str;
		size_t _size;
		size_t _capacity}
	;}

extern
void stringTest1 ();extern
void stringTest2 ();#

接下来是mystring.cpp

pragmawarning (:disable4996)#
include#
include"mystring.h"#
include::

gyj::stringstring(constchar *) sif
{
	( nullptr== ) sassert
	{
		(0);}
	=
	_size strlen ()s;=
	_capacity ; _size=
	_str new char [+_capacity1 ];strcpy
	(,_str) s;}
::
gyj::stringstring(const& string) str:
_str(nullptr),
_size (0),
_capacity (0)temp
{
	string (.str)_str;::
	stdswap(,_str. temp)_str;=
	_size . str;_size=
	_capacity . str;_capacity}
::

gyj&string:: gyj::stringoperator=(const& string) strif
{
	( this!= & )strtemp
	{
		string (.str)_str;::
		stdswap(,_str. temp)_str;=
		_size . str;_size=
		_capacity . str;_capacity}
	return
	* this;}
::

gyjstring::~string()if
{
	( )_strdelete
	{
		[]; _str=
		_str nullptr ;}
	}
///迭代器相关/
::
gyj::string::iterator gyj::stringbegin()return
{
	; _str}
::

gyj::string::iterator gyj::stringend()return
{
	+ _str ; _size}
///容量相关//

::
size_t gyj::stringsize()constreturn
{
	; _size}
::
size_t gyj::stringcapacity()constreturn
{
	; _capacity}
bool

:: gyj::stringempty()constif
{
	( 0== ) _sizereturn
	{
		true ;}
	return
	false ;}
void
:: gyj::stringresize(,size_t nchar ) cif
{
	( )n > _sizeif
	{
		( )n > _capacityreserve
		{
			()n;}
		memset
		(+_str , _size, c- n ) _size;}
	=
	_size ; n[
	_str]_size= ';' }void
::
:: gyjreservestring()ifsize_t n(
{
	) charn > _capacity*
	{
		=new temp char [ +1n ] ;strcpy(
		,)temp; _strdelete[
		];= _str;
		_str = temp;
		_capacity } n}
	///元素访问相关//
char
&
:::: gyjoperatorstring[]()assertsize_t index(
{
	<)index ; _sizereturn[
	] _str;index}const
char
& :::: gyjoperatorstring[]()constsize_t indexassert(
{
	<)index ; _sizereturn[
	] _str;index}///修改相关的///
void
::
:: gyjpush_backstring(char)if c(
{
	== )_size //扩容 _capacityreserve
	{
		(
		*2_capacity ) ;}[
	++
	_str]_size=; [ c]
	_str=_size';' } ::&
::
gyj::stringoperator gyj+=string(char)push_back( c)
{
	;returnc*this
	; }void::
::
append gyj(stringconstchar*) if( str+
{
	strlen (_size))//扩容str= >= _capacity*
	{
		2
		size_t newcapacity + _capacity strlen ( > _size ) ?*str2 : _capacity + strlen ( _size ) +1str; reserve ()
		;}newcapacitystrcat(
	,
	);_str+= strstrlen(
	_size ) ;}str::&
::
gyj::stringoperator gyj+=string(constchar*) append( str)
{
	;returnstr*this
	; }void::
::
clear gyj(string)[0]
{
	_str=';'= 0 ;}
	_size void ::::
swap
( gyj&string)::swapstring( s,
{
	std.);_str:: sswap_str(,
	std.);_size:: sswap_size(,
	std.);_capacity} sconst_capacitychar*
::
:: c_str( gyj)stringconstreturn;}// <<重载
{
	:: _str&
::




operator
std<<ostream( gyj::&,conststd::ostream& _cout) << gyj.string; sreturn
{
	_cout ; s}_str/其他//

	bool _cout::
::

operator
< gyj(stringconst&)int= stringstrcmp s(
{
	, ret . );_strif s(_strret<
	0 )return true ;}
	{
		return false;
	}
	bool ::::
operator
<= gyj(stringconst&)return! string( s*
{
	this );}bool>s::::
operator
( gyjconststring&)>int= stringstrcmp s(
{
	, ret . );_strif s(_str0)
	return trueret > ;}
	{
		return false;
	}
	bool ::::
operator
( gyjconststring&)>=return! string( s*
{
	this <);} bool s::::
operator
== gyj(stringconst&)int= stringstrcmp s(
{
	, ret . );_strif s(_str==0
	) returnret true ;}
	{
		return false;
	}
	bool ::::
operator
!= gyj(stringconst&)return! string( s*
{
	this ==);} // 返回c在string中第一次出现的位置 s::::
find

(
size_t gyjcharstring,)constfor c( size_t pos= ;
{
	< ;size_t i ++ pos) i if _size( i==[
	{
		] )c return _str;i}}
		{
			return i-
		1
	;
	} // 返回子串s在string中第一次出现的位置::::
find
(
size_t gyjconststringchar*,) constassert s( size_t pos) ;
{
	assert(s<)
	;constpos char _size*=
	+ ;while src ( _str * pos)
	const char*src=
	{
		; constchar match * s=
		; while( cur * src&&
		* ==*match ) ++match ; ++cur;
		{
			}matchif
			(cur*
		==
		')' return-match ; }else
		{
			++ src ; _str}
		}
		return
		{
			-src1
		;
	}
	// 在pos位置上插入字符c/字符串str,并返回该字符的位置 ::&::
::
insert
gyj(string, gyjcharstring)assert(size_t pos<= ) c;
{
	if(pos == _size)//扩容
	char *_size = _capacitynew
	{
		char
		[ *newstr 2 + 1]_capacity ; strcpy ( ,);
		delete[newstr] _str;=
		;*=2 _str;
		_str } newstr//移数据
		_capacity for (int
	=
	;
	( int) i ; _size-- i >= )[+pos1 ]i=
	{
		_str[i ] ;} [ _str]i=;

	++
	_str;posreturn * cthis
	_size;}

	:: &::::

insert
gyj(string, gyjconststringchar*)size_t pos= strlen () str;
{
	size_t len if (+str)//扩容
	//扩容 char_size * len > _capacity=new
	{
		char
		[ *newstr 2 + 1]_capacity ; strcpy ( ,);
		delete[newstr] _str;=
		;*=2 _str;
		_str } newstr//后移数据
		_capacity for (int
	=

	;
	( int) i ; _size-- i >= )[+pos] =i[
	{
		_str]len ; i} //拷贝字符串 _strwhilei(*
	!=

	')'
	[ ++]str = *++
	{
		_str;pos}+= ; returnstr*this
	;
	_size } len// 删除pos位置上的元素,并返回该元素的下一个位置
	:: &::::
erase

(
gyj,string) gyjassertstring(<)size_t pos; size_t lenif
{
	(+pos ) _size//pos位置之后全为0[

	] =pos ';' len >= _size=;
	{
		_str}poselse strcpy (+
		_size , pos+
	+
	)
	{
		;-=_str ; pos} _str return pos * lenthis;
		_size } lenvoid
	stringTest1
	( )::s1

(


"hello" );::
{
	gyjs2string ();::;
	gyj=string ;::s1<<.
	gyjsizestring s3(
	s3 ) s2<<

	std::cout ;s1::<<.capacity ( std)endl<<
	std::cout ; s1::<<.empty ( std)endl<<
	std::cout ; s1.resize(3 ) std;endl.

	s1resize(10);.
	s2resize(15,'A')
	s3;.reserve(4 );.

	s1reserve(20);.
	s2reserve(100);::
	s3<<[0]<<::

	std;cout :: s1<<[3 ] std<<endl::
	std;cout const s2::s4( "aaaaaa" std)endl;
	char gyj=string [3];//_CrtDumpMemoryLeaks();
	} ch void s4stringTest2()::

	s1
(

"Hello" );.
{
	gyjpush_backstring (' ');.

	s1append("World");+=
	s1'!';+="~~~~";::
	s1 << .c_str
	s1 ( )<<
	
	std::cout ; s1::s2("12345" ) std;endl.

	gyjswapstring ();.clear
	s1();s2_CrtDumpMemoryLeaks(
	s1);}
	[+++]

[+++]

具体代可以参考我的码云

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 1782, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

文章目录

1.浅拷贝问题解决 1.1浅拷贝存在的问题


对于浅拷贝,主要存在于拷贝构造和赋值运算符重载的过程中,下面给出一段代码结合分析

#pragma warning(disable:4996)
#include

using namespace std;

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

	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}

private:
	char* _str;
};

void StringTest()
{
	String s1("Hello");
	String s2(s1);
}

int main()
{
	StringTest();
	return 0;
}

执行时程序直接崩溃


下面分析一下原因:
上述的崩溃时由于浅拷贝导致的多次释放问题

再来看下面的代码快:

分析:
本次报错是因为执行s2 = s1时,导致内存泄漏以及多次释放的问题,具体看下面的分析

总结一下:
浅拷贝会导致①内存泄露②多次释放同一块空间
这些错误都是极其严重的,我们务必要避免!

接下来就来探讨一下如何解决这些问题:

1.2通过深拷贝的方式解决浅拷贝问题

本质:让每一个对象都拥有一份独立的资源

传统版解决方式
  1. 解决拷贝构造
  2. 解决赋值运算符重载

    下面给出解决的完整代码
#pragma warning(disable:4996)
#include

using namespace std;

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(new char[strlen(s._str)+1])
	{
		strcpy(_str, s._str);
	}

	String& operator=(const String& s)
	{
		if (this != &s)
		{
			char* temp = new char[strlen(s._str) + 1];
			strcpy(temp, s._str);
			delete[] _str;
			_str = temp;
		}
		return *this;
	}

	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}

private:
	char* _str;
};

void StringTest()
{
	String s1("Hello");
	String s2("World");
	s2 = s1;

	//String s2(s1);
}

int main()
{
	StringTest();
	return 0;
}
现代版解决方式

仔细观察上面的代码,我们发现其实重复的 *** 作很多,比如每次申请新空间,拷贝元素。



现代版的方式就是采用巧妙地代码复用,将繁琐的 *** 作简洁化
具体修改在拷贝构造和赋值运算符重载处

下面我们一一解决

  1. 拷贝构造的优化
  2. 赋值运算符重载的优化

    以上便是对之前代码的一些优化!
1.3通过写时拷贝解决 理解写时拷贝方法

写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。



引用计数:用来记录资源使用者的个数。



在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,
当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,
如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;
否则就不能释放,因为还有其他对象在使用该资源。



下面使用图解的方式解释一下:

OK,看了上面的解法,有没有感觉有点问题呢?
上面的过程并没有涉及到对象内容修改,假设我现在想将上面s2对象的内容修改为“World”;由于s1 s2共用一块内存空间,所以修改s2的同时s1的内容也被修改了
但是很明显,这不是我的本意,也不符合规矩。


那如何解决这个棘手问题呢?

其实上面的这个过程还没有涉及到我们的写时拷贝。



所谓的写时拷贝是在上面的基础上解决修改(写)内容的时候发生的问题
具体步骤如下:
假设我们现在已经处于上面图示的场景,即s1和s2共用同一块内存空间,计数器此时为2
现在我想要将s2的内容改为“World”,需要执行以下步骤:
(1)为s2对象开辟新的空间
(2)将原来的空间的计数器值减减
(3)将s1对象的内容拷贝至为s2新开辟的空间中,并将s2中计数器值设置为1
(4)s2在新空间内进行修改 *** 作
下面通过图示的方式再次演示该过程:

以上便是写时拷贝的粗略思路,具体细节内容等到后续总结,目前处于扫盲状态。


1.4验证不同平台的string类是通过什么方式解决浅拷贝的

验证思路:
(1)实例化一个对象s1,长度大于15;
注意:长度必须大于15,否则无法测试出正确的结果。


因为在Windows下,string类中维护着一个空间为16的字符数组,因此小于等于15的字符串直接被存储在数组内,并不会申请空间,也就无法验证。



(2)通过拷贝构造实例化对象s2
(3)打印s1和s2对象的地址,观察地址值是否一样
如果一样 -----> 写时拷贝
如果不一样 ----> 深拷贝
验证代码如下:

void TestCopy()
{
	string s1(20, 'A');
	string s2(s1);

	printf("&s1 is %p\n",s1.c_str());
	printf("&s2 is %p\n", s2.c_str());
}

int main()
{
	TestCopy();
	return 0;
}

1、在Windows平台下的VS2013环境中

结论:vs2013中string是按照深拷贝实现的
2、在Linux平台下

结论:Linux中string是按照写时拷贝实现的

2、string类实现

有了前面的知识做铺垫,这里直接给出代码,模拟实现只是对主要的方法进行模拟,并不是完全实现一个string容器

//mystring.h
#pragma once

#include

namespace gyj
{
	class string
	{
		friend std::ostream& operator<<(std::ostream& _cout, const gyj::string& s);
	public:
		typedef char* iterator;
	public:
		/构造和析构///
		string(const char* s = "");
		string(const string& str);
		string& operator=(const string& str);
		~string();
		///迭代器相关/
		iterator begin();
		iterator end();
		///容量相关//
		size_t size()const;
		size_t capacity()const;
		bool empty()const;
		void resize(size_t n, char c = ')';void
		reserve ()size_t n;///元素访问相关//

		char
		&operator []()size_t index;const
		char &operator []()size_t indexconst;///修改相关的///
		void
		push_back (char) c;&
		stringoperator +=(char) c;void
		append (constchar *) str;&
		stringoperator +=(constchar *) str;void
		clear ();void
		swap (&string) s;const
		char *c_str ()const;/其他//
		bool
		operator <(const& string) s;bool
		operator <=(const& string) s;bool
		operator (>const& string) s;bool
		operator (>=const& string) s;bool
		operator ==(const& string) s;bool
		operator !=(const& string) s;// 返回c在string中第一次出现的位置


		find
		size_t (char, c= size_t pos 0 )const ;// 返回子串s在string中第一次出现的位置
		find
		size_t (constchar *, s= size_t pos 0 )const ;// 在pos位置上插入字符c/字符串str,并返回该字符的位置
		&
		stringinsert (,size_t poschar ) c;&
		stringinsert (,size_t posconst char *) str;// 删除pos位置上的元素,并返回该元素的下一个位置


		&
		stringerase (,size_t pos) size_t len;private
	:char
		*; _str;
		size_t _size;
		size_t _capacity}
	;}

extern
void stringTest1 ();extern
void stringTest2 ();#

接下来是mystring.cpp

pragmawarning (:disable4996)#
include#
include"mystring.h"#
include::

gyj::stringstring(constchar *) sif
{
	( nullptr== ) sassert
	{
		(0);}
	=
	_size strlen ()s;=
	_capacity ; _size=
	_str new char [+_capacity1 ];strcpy
	(,_str) s;}
::
gyj::stringstring(const& string) str:
_str(nullptr),
_size (0),
_capacity (0)temp
{
	string (.str)_str;::
	stdswap(,_str. temp)_str;=
	_size . str;_size=
	_capacity . str;_capacity}
::

gyj&string:: gyj::stringoperator=(const& string) strif
{
	( this!= & )strtemp
	{
		string (.str)_str;::
		stdswap(,_str. temp)_str;=
		_size . str;_size=
		_capacity . str;_capacity}
	return
	* this;}
::

gyjstring::~string()if
{
	( )_strdelete
	{
		[]; _str=
		_str nullptr ;}
	}
///迭代器相关/
::
gyj::string::iterator gyj::stringbegin()return
{
	; _str}
::

gyj::string::iterator gyj::stringend()return
{
	+ _str ; _size}
///容量相关//

::
size_t gyj::stringsize()constreturn
{
	; _size}
::
size_t gyj::stringcapacity()constreturn
{
	; _capacity}
bool

:: gyj::stringempty()constif
{
	( 0== ) _sizereturn
	{
		true ;}
	return
	false ;}
void
:: gyj::stringresize(,size_t nchar ) cif
{
	( )n > _sizeif
	{
		( )n > _capacityreserve
		{
			()n;}
		memset
		(+_str , _size, c- n ) _size;}
	=
	_size ; n[
	_str]_size= ';' }void
::
:: gyjreservestring()ifsize_t n(
{
	) charn > _capacity*
	{
		=new temp char [ +1n ] ;strcpy(
		,)temp; _strdelete[
		];= _str;
		_str = temp;
		_capacity } n}
	///元素访问相关//
char
&
:::: gyjoperatorstring[]()assertsize_t index(
{
	<)index ; _sizereturn[
	] _str;index}const
char
& :::: gyjoperatorstring[]()constsize_t indexassert(
{
	<)index ; _sizereturn[
	] _str;index}///修改相关的///
void
::
:: gyjpush_backstring(char)if c(
{
	== )_size //扩容 _capacityreserve
	{
		(
		*2_capacity ) ;}[
	++
	_str]_size=; [ c]
	_str=_size';' } ::&
::
gyj::stringoperator gyj+=string(char)push_back( c)
{
	;returnc*this
	; }void::
::
append gyj(stringconstchar*) if( str+
{
	strlen (_size))//扩容str= >= _capacity*
	{
		2
		size_t newcapacity + _capacity strlen ( > _size ) ?*str2 : _capacity + strlen ( _size ) +1str; reserve ()
		;}newcapacitystrcat(
	,
	);_str+= strstrlen(
	_size ) ;}str::&
::
gyj::stringoperator gyj+=string(constchar*) append( str)
{
	;returnstr*this
	; }void::
::
clear gyj(string)[0]
{
	_str=';'= 0 ;}
	_size void ::::
swap
( gyj&string)::swapstring( s,
{
	std.);_str:: sswap_str(,
	std.);_size:: sswap_size(,
	std.);_capacity} sconst_capacitychar*
::
:: c_str( gyj)stringconstreturn;}// <<重载
{
	:: _str&
::




operator
std<<ostream( gyj::&,conststd::ostream& _cout) << gyj.string; sreturn
{
	_cout ; s}_str/其他//

	bool _cout::
::

operator
< gyj(stringconst&)int= stringstrcmp s(
{
	, ret . );_strif s(_strret<
	0 )return true ;}
	{
		return false;
	}
	bool ::::
operator
<= gyj(stringconst&)return! string( s*
{
	this );}bool>s::::
operator
( gyjconststring&)>int= stringstrcmp s(
{
	, ret . );_strif s(_str0)
	return trueret > ;}
	{
		return false;
	}
	bool ::::
operator
( gyjconststring&)>=return! string( s*
{
	this <);} bool s::::
operator
== gyj(stringconst&)int= stringstrcmp s(
{
	, ret . );_strif s(_str==0
	) returnret true ;}
	{
		return false;
	}
	bool ::::
operator
!= gyj(stringconst&)return! string( s*
{
	this ==);} // 返回c在string中第一次出现的位置 s::::
find

(
size_t gyjcharstring,)constfor c( size_t pos= ;
{
	< ;size_t i ++ pos) i if _size( i==[
	{
		] )c return _str;i}}
		{
			return i-
		1
	;
	} // 返回子串s在string中第一次出现的位置::::
find
(
size_t gyjconststringchar*,) constassert s( size_t pos) ;
{
	assert(s<)
	;constpos char _size*=
	+ ;while src ( _str * pos)
	const char*src=
	{
		; constchar match * s=
		; while( cur * src&&
		* ==*match ) ++match ; ++cur;
		{
			}matchif
			(cur*
		==
		')' return-match ; }else
		{
			++ src ; _str}
		}
		return
		{
			-src1
		;
	}
	// 在pos位置上插入字符c/字符串str,并返回该字符的位置 ::&::
::
insert
gyj(string, gyjcharstring)assert(size_t pos<= ) c;
{
	if(pos == _size)//扩容
	char *_size = _capacitynew
	{
		char
		[ *newstr 2 + 1]_capacity ; strcpy ( ,);
		delete[newstr] _str;=
		;*=2 _str;
		_str } newstr//移数据
		_capacity for (int
	=
	;
	( int) i ; _size-- i >= )[+pos1 ]i=
	{
		_str[i ] ;} [ _str]i=;

	++
	_str;posreturn * cthis
	_size;}

	:: &::::

insert
gyj(string, gyjconststringchar*)size_t pos= strlen () str;
{
	size_t len if (+str)//扩容
	//扩容 char_size * len > _capacity=new
	{
		char
		[ *newstr 2 + 1]_capacity ; strcpy ( ,);
		delete[newstr] _str;=
		;*=2 _str;
		_str } newstr//后移数据
		_capacity for (int
	=

	;
	( int) i ; _size-- i >= )[+pos] =i[
	{
		_str]len ; i} //拷贝字符串 _strwhilei(*
	!=

	')'
	[ ++]str = *++
	{
		_str;pos}+= ; returnstr*this
	;
	_size } len// 删除pos位置上的元素,并返回该元素的下一个位置
	:: &::::
erase

(
gyj,string) gyjassertstring(<)size_t pos; size_t lenif
{
	(+pos ) _size//pos位置之后全为0[

	] =pos ';' len >= _size=;
	{
		_str}poselse strcpy (+
		_size , pos+
	+
	)
	{
		;-=_str ; pos} _str return pos * lenthis;
		_size } lenvoid
	stringTest1
	( )::s1

(


"hello" );::
{
	gyjs2string ();::;
	gyj=string ;::s1<<.
	gyjsizestring s3(
	s3 ) s2<<

	std::cout ;s1::<<.capacity ( std)endl<<
	std::cout ; s1::<<.empty ( std)endl<<
	std::cout ; s1.resize(3 ) std;endl.

	s1resize(10);.
	s2resize(15,'A')
	s3;.reserve(4 );.

	s1reserve(20);.
	s2reserve(100);::
	s3<<[0]<<::

	std;cout :: s1<<[3 ] std<<endl::
	std;cout const s2::s4( "aaaaaa" std)endl;
	char gyj=string [3];//_CrtDumpMemoryLeaks();
	} ch void s4stringTest2()::

	s1
(

"Hello" );.
{
	gyjpush_backstring (' ');.

	s1append("World");+=
	s1'!';+="~~~~";::
	s1 << .c_str
	s1 ( )<<
	
	std::cout ; s1::s2("12345" ) std;endl.

	gyjswapstring ();.clear
	s1();s2_CrtDumpMemoryLeaks(
	s1);}
	

[+++]

具体代可以参考我的码云

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
【C++】string容器模拟实现_C_内存溢出

【C++】string容器模拟实现

【C++】string容器模拟实现,第1张

文章目录
  • 1.浅拷贝问题解决
    • 1.1浅拷贝存在的问题
    • 1.2通过深拷贝的方式解决浅拷贝问题
      • 传统版解决方式
      • 现代版解决方式
    • 1.3通过写时拷贝解决
      • 理解写时拷贝方法
    • 1.4验证不同平台的string类是通过什么方式解决浅拷贝的
  • 2、string类实现

1.浅拷贝问题解决 1.1浅拷贝存在的问题


对于浅拷贝,主要存在于拷贝构造和赋值运算符重载的过程中,下面给出一段代码结合分析

#pragma warning(disable:4996)
#include

using namespace std;

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

	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}

private:
	char* _str;
};

void StringTest()
{
	String s1("Hello");
	String s2(s1);
}

int main()
{
	StringTest();
	return 0;
}

执行时程序直接崩溃


下面分析一下原因:
上述的崩溃时由于浅拷贝导致的多次释放问题

再来看下面的代码快:

分析:
本次报错是因为执行s2 = s1时,导致内存泄漏以及多次释放的问题,具体看下面的分析

总结一下:
浅拷贝会导致①内存泄露②多次释放同一块空间
这些错误都是极其严重的,我们务必要避免!

接下来就来探讨一下如何解决这些问题:

1.2通过深拷贝的方式解决浅拷贝问题

本质:让每一个对象都拥有一份独立的资源

传统版解决方式
  1. 解决拷贝构造
  2. 解决赋值运算符重载

    下面给出解决的完整代码
#pragma warning(disable:4996)
#include

using namespace std;

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(new char[strlen(s._str)+1])
	{
		strcpy(_str, s._str);
	}

	String& operator=(const String& s)
	{
		if (this != &s)
		{
			char* temp = new char[strlen(s._str) + 1];
			strcpy(temp, s._str);
			delete[] _str;
			_str = temp;
		}
		return *this;
	}

	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}

private:
	char* _str;
};

void StringTest()
{
	String s1("Hello");
	String s2("World");
	s2 = s1;

	//String s2(s1);
}

int main()
{
	StringTest();
	return 0;
}
现代版解决方式

仔细观察上面的代码,我们发现其实重复的 *** 作很多,比如每次申请新空间,拷贝元素。



现代版的方式就是采用巧妙地代码复用,将繁琐的 *** 作简洁化
具体修改在拷贝构造和赋值运算符重载处

下面我们一一解决

  1. 拷贝构造的优化
  2. 赋值运算符重载的优化

    以上便是对之前代码的一些优化!
1.3通过写时拷贝解决 理解写时拷贝方法

写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。



引用计数:用来记录资源使用者的个数。



在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,
当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,
如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;
否则就不能释放,因为还有其他对象在使用该资源。



下面使用图解的方式解释一下:

OK,看了上面的解法,有没有感觉有点问题呢?
上面的过程并没有涉及到对象内容修改,假设我现在想将上面s2对象的内容修改为“World”;由于s1 s2共用一块内存空间,所以修改s2的同时s1的内容也被修改了
但是很明显,这不是我的本意,也不符合规矩。


那如何解决这个棘手问题呢?

其实上面的这个过程还没有涉及到我们的写时拷贝。



所谓的写时拷贝是在上面的基础上解决修改(写)内容的时候发生的问题
具体步骤如下:
假设我们现在已经处于上面图示的场景,即s1和s2共用同一块内存空间,计数器此时为2
现在我想要将s2的内容改为“World”,需要执行以下步骤:
(1)为s2对象开辟新的空间
(2)将原来的空间的计数器值减减
(3)将s1对象的内容拷贝至为s2新开辟的空间中,并将s2中计数器值设置为1
(4)s2在新空间内进行修改 *** 作
下面通过图示的方式再次演示该过程:

以上便是写时拷贝的粗略思路,具体细节内容等到后续总结,目前处于扫盲状态。


1.4验证不同平台的string类是通过什么方式解决浅拷贝的

验证思路:
(1)实例化一个对象s1,长度大于15;
注意:长度必须大于15,否则无法测试出正确的结果。


因为在Windows下,string类中维护着一个空间为16的字符数组,因此小于等于15的字符串直接被存储在数组内,并不会申请空间,也就无法验证。



(2)通过拷贝构造实例化对象s2
(3)打印s1和s2对象的地址,观察地址值是否一样
如果一样 -----> 写时拷贝
如果不一样 ----> 深拷贝
验证代码如下:

void TestCopy()
{
	string s1(20, 'A');
	string s2(s1);

	printf("&s1 is %p\n",s1.c_str());
	printf("&s2 is %p\n", s2.c_str());
}

int main()
{
	TestCopy();
	return 0;
}

1、在Windows平台下的VS2013环境中

结论:vs2013中string是按照深拷贝实现的
2、在Linux平台下

结论:Linux中string是按照写时拷贝实现的

2、string类实现

有了前面的知识做铺垫,这里直接给出代码,模拟实现只是对主要的方法进行模拟,并不是完全实现一个string容器

//mystring.h
#pragma once

#include

namespace gyj
{
	class string
	{
		friend std::ostream& operator<<(std::ostream& _cout, const gyj::string& s);
	public:
		typedef char* iterator;
	public:
		/构造和析构///
		string(const char* s = "");
		string(const string& str);
		string& operator=(const string& str);
		~string();
		///迭代器相关/
		iterator begin();
		iterator end();
		///容量相关//
		size_t size()const;
		size_t capacity()const;
		bool empty()const;
		void resize(size_t n, char c = ')';void
		reserve ()size_t n;///元素访问相关//

		char
		&operator []()size_t index;const
		char &operator []()size_t indexconst;///修改相关的///
		void
		push_back (char) c;&
		stringoperator +=(char) c;void
		append (constchar *) str;&
		stringoperator +=(constchar *) str;void
		clear ();void
		swap (&string) s;const
		char *c_str ()const;/其他//
		bool
		operator <(const& string) s;bool
		operator <=(const& string) s;bool
		operator (>const& string) s;bool
		operator (>=const& string) s;bool
		operator ==(const& string) s;bool
		operator !=(const& string) s;// 返回c在string中第一次出现的位置


		find
		size_t (char, c= size_t pos 0 )const ;// 返回子串s在string中第一次出现的位置
		find
		size_t (constchar *, s= size_t pos 0 )const ;// 在pos位置上插入字符c/字符串str,并返回该字符的位置
		&
		stringinsert (,size_t poschar ) c;&
		stringinsert (,size_t posconst char *) str;// 删除pos位置上的元素,并返回该元素的下一个位置


		&
		stringerase (,size_t pos) size_t len;private
	:char
		*; _str;
		size_t _size;
		size_t _capacity}
	;}

extern
void stringTest1 ();extern
void stringTest2 ();#

接下来是mystring.cpp

pragmawarning (:disable4996)#
include#
include"mystring.h"#
include::

gyj::stringstring(constchar *) sif
{
	( nullptr== ) sassert
	{
		(0);}
	=
	_size strlen ()s;=
	_capacity ; _size=
	_str new char [+_capacity1 ];strcpy
	(,_str) s;}
::
gyj::stringstring(const& string) str:
_str(nullptr),
_size (0),
_capacity (0)temp
{
	string (.str)_str;::
	stdswap(,_str. temp)_str;=
	_size . str;_size=
	_capacity . str;_capacity}
::

gyj&string:: gyj::stringoperator=(const& string) strif
{
	( this!= & )strtemp
	{
		string (.str)_str;::
		stdswap(,_str. temp)_str;=
		_size . str;_size=
		_capacity . str;_capacity}
	return
	* this;}
::

gyjstring::~string()if
{
	( )_strdelete
	{
		[]; _str=
		_str nullptr ;}
	}
///迭代器相关/
::
gyj::string::iterator gyj::stringbegin()return
{
	; _str}
::

gyj::string::iterator gyj::stringend()return
{
	+ _str ; _size}
///容量相关//

::
size_t gyj::stringsize()constreturn
{
	; _size}
::
size_t gyj::stringcapacity()constreturn
{
	; _capacity}
bool

:: gyj::stringempty()constif
{
	( 0== ) _sizereturn
	{
		true ;}
	return
	false ;}
void
:: gyj::stringresize(,size_t nchar ) cif
{
	( )n > _sizeif
	{
		( )n > _capacityreserve
		{
			()n;}
		memset
		(+_str , _size, c- n ) _size;}
	=
	_size ; n[
	_str]_size= ';' }void
::
:: gyjreservestring()ifsize_t n(
{
	) charn > _capacity*
	{
		=new temp char [ +1n ] ;strcpy(
		,)temp; _strdelete[
		];= _str;
		_str = temp;
		_capacity } n}
	///元素访问相关//
char
&
:::: gyjoperatorstring[]()assertsize_t index(
{
	<)index ; _sizereturn[
	] _str;index}const
char
& :::: gyjoperatorstring[]()constsize_t indexassert(
{
	<)index ; _sizereturn[
	] _str;index}///修改相关的///
void
::
:: gyjpush_backstring(char)if c(
{
	== )_size //扩容 _capacityreserve
	{
		(
		*2_capacity ) ;}[
	++
	_str]_size=; [ c]
	_str=_size';' } ::&
::
gyj::stringoperator gyj+=string(char)push_back( c)
{
	;returnc*this
	; }void::
::
append gyj(stringconstchar*) if( str+
{
	strlen (_size))//扩容str= >= _capacity*
	{
		2
		size_t newcapacity + _capacity strlen ( > _size ) ?*str2 : _capacity + strlen ( _size ) +1str; reserve ()
		;}newcapacitystrcat(
	,
	);_str+= strstrlen(
	_size ) ;}str::&
::
gyj::stringoperator gyj+=string(constchar*) append( str)
{
	;returnstr*this
	; }void::
::
clear gyj(string)[0]
{
	_str=';'= 0 ;}
	_size void ::::
swap
( gyj&string)::swapstring( s,
{
	std.);_str:: sswap_str(,
	std.);_size:: sswap_size(,
	std.);_capacity} sconst_capacitychar*
::
:: c_str( gyj)stringconstreturn;}// <<重载
{
	:: _str&
::




operator
std<<ostream( gyj::&,conststd::ostream& _cout) << gyj.string; sreturn
{
	_cout ; s}_str/其他//

	bool _cout::
::

operator
< gyj(stringconst&)int= stringstrcmp s(
{
	, ret . );_strif s(_strret<
	0 )return true ;}
	{
		return false;
	}
	bool ::::
operator
<= gyj(stringconst&)return! string( s*
{
	this );}bool>s::::
operator
( gyjconststring&)>int= stringstrcmp s(
{
	, ret . );_strif s(_str0)
	return trueret > ;}
	{
		return false;
	}
	bool ::::
operator
( gyjconststring&)>=return! string( s*
{
	this <);} bool s::::
operator
== gyj(stringconst&)int= stringstrcmp s(
{
	, ret . );_strif s(_str==0
	) returnret true ;}
	{
		return false;
	}
	bool ::::
operator
!= gyj(stringconst&)return! string( s*
{
	this ==);} // 返回c在string中第一次出现的位置 s::::
find

(
size_t gyjcharstring,)constfor c( size_t pos= ;
{
	< ;size_t i ++ pos) i if _size( i==[
	{
		] )c return _str;i}}
		{
			return i-
		1
	;
	} // 返回子串s在string中第一次出现的位置::::
find
(
size_t gyjconststringchar*,) constassert s( size_t pos) ;
{
	assert(s<)
	;constpos char _size*=
	+ ;while src ( _str * pos)
	const char*src=
	{
		; constchar match * s=
		; while( cur * src&&
		* ==*match ) ++match ; ++cur;
		{
			}matchif
			(cur*
		==
		')' return-match ; }else
		{
			++ src ; _str}
		}
		return
		{
			-src1
		;
	}
	// 在pos位置上插入字符c/字符串str,并返回该字符的位置 ::&::
::
insert
gyj(string, gyjcharstring)assert(size_t pos<= ) c;
{
	if(pos == _size)//扩容
	char *_size = _capacitynew
	{
		char
		[ *newstr 2 + 1]_capacity ; strcpy ( ,);
		delete[newstr] _str;=
		;*=2 _str;
		_str } newstr//移数据
		_capacity for (int
	=
	;
	( int) i ; _size-- i >= )[+pos1 ]i=
	{
		_str[i ] ;} [ _str]i=;

	++
	_str;posreturn * cthis
	_size;}

	:: &::::

insert
gyj(string, gyjconststringchar*)size_t pos= strlen () str;
{
	size_t len if (+str)//扩容
	//扩容 char_size * len > _capacity=new
	{
		char
		[ *newstr 2 + 1]_capacity ; strcpy ( ,);
		delete[newstr] _str;=
		;*=2 _str;
		_str } newstr//后移数据
		_capacity for (int
	=

	;
	( int) i ; _size-- i >= )[+pos] =i[
	{
		_str]len ; i} //拷贝字符串 _strwhilei(*
	!=

	')'
	[ ++]str = *++
	{
		_str;pos}+= ; returnstr*this
	;
	_size } len// 删除pos位置上的元素,并返回该元素的下一个位置
	:: &::::
erase

(
gyj,string) gyjassertstring(<)size_t pos; size_t lenif
{
	(+pos ) _size//pos位置之后全为0[

	] =pos ';' len >= _size=;
	{
		_str}poselse strcpy (+
		_size , pos+
	+
	)
	{
		;-=_str ; pos} _str return pos * lenthis;
		_size } lenvoid
	stringTest1
	( )::s1

(


"hello" );::
{
	gyjs2string ();::;
	gyj=string ;::s1<<.
	gyjsizestring s3(
	s3 ) s2<<

	std::cout ;s1::<<.capacity ( std)endl<<
	std::cout ; s1::<<.empty ( std)endl<<
	std::cout ; s1.resize(3 ) std;endl.

	s1resize(10);.
	s2resize(15,'A')
	s3;.reserve(4 );.

	s1reserve(20);.
	s2reserve(100);::
	s3<<[0]<<::

	std;cout :: s1<<[3 ] std<<endl::
	std;cout const s2::s4( "aaaaaa" std)endl;
	char gyj=string [3];//_CrtDumpMemoryLeaks();
	} ch void s4stringTest2()::

	s1
(

"Hello" );.
{
	gyjpush_backstring (' ');.

	s1append("World");+=
	s1'!';+="~~~~";::
	s1 << .c_str
	s1 ( )<<
	
	std::cout ; s1::s2("12345" ) std;endl.

	gyjswapstring ();.clear
	s1();s2_CrtDumpMemoryLeaks(
	s1);}
	


具体代可以参考我的码云

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/584692.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-12
下一篇 2022-04-12

发表评论

登录后才能评论

评论列表(0条)

保存