99偷拍视频精品区一区二,口述久久久久久久久久久久,国产精品夫妇激情啪发布,成人永久免费网站在线观看,国产精品高清免费在线,青青草在线观看视频观看,久久久久久国产一区,天天婷婷久久18禁,日韩动漫av在线播放直播

【c++】STL--string-創新互聯

前言

? 最開始我們學習c語言的時候,我們發現刷題或者寫代碼都是比較麻煩的,如果說用c語言造一輛車,那么我需要用c語言先把輪子造好--各個零件,當我們造好之后再組裝。那么c++則是造好了輪子,只需要我們組裝就好了。這里的的STL里有各個組件,我只要熟悉的掌握,使用即可。

目前創新互聯已為近1000家的企業提供了網站建設、域名、網站空間網站運營、企業網站設計、建湖網站維護等服務,公司將堅持客戶導向、應用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協力一起成長,共同發展。

網上有句話說:“不懂STL,不要說你會C++”。STL是C++中的優秀作品,有了它的陪伴,許多底層的數據結構以及算法都不需要自己重新造輪子,站在前人的肩膀上,健步如飛的快速開發。

所以對于學習c++而言,學好STL是很有必要的。STL統稱為:“Standard TemplateLibrary 標準模板庫”,STL提供了有六大組件,包括了容器,算法,迭代器,仿函數,適配器以及空間適配器。這篇文章主要是講解STL容器中的string。這些組件雖然聽起來很陌生,但是熟悉過后你會發現其實就是扮演的角色一樣,名字不一樣而已。

文章的路程大概就是:我們先了解STL,然后再查看標準庫中的string類,先對對他們的使用進行理解,最后為了更好的使用和鞏固,在對string重要的接口進行模擬實現。模擬實現的時候進行分析,理解。

目錄

前言

STL

什么是STL

STL的版本

STL的缺陷

標準庫中的string類

了解string類

string類的常用接口說明

string類對象的容量操作

string類對象的訪問及遍歷操作

string類對象的修改操作

string類非成員函數

string類的模擬實現

經典的string類問題

淺拷貝/深拷貝

string類的模擬實現


STL 什么是STL

STL(standard template libaray-標準模板庫):是C++標準庫的重要組成部分,不僅是一個可復用的組件庫,而且 是一個包羅數據結構與算法的軟件框架。

STL的版本

原始版本

Alexander Stepanov、Meng Lee 在惠普實驗室完成的原始版本,本著開源精神,他們聲明允許任何人任意 運用、拷貝、修改、傳播、商業使用這些代碼,無需付費。唯一的條件就是也需要向原始版本一樣做開源使 用。 HP 版本--所有STL實現版本的始祖。

P. J. 版本

由P. J. Plauger開發,繼承自HP版本,被Windows Visual C++采用,不能公開或修改,缺陷:可讀性比較低, 符號命名比較怪異。

RW版本

由Rouge Wage公司開發,繼承自HP版本,被C+ + Builder 采用,不能公開或修改,可讀性一般。

SGI版本

由Silicon Graphics Computer Systems,Inc公司開發,繼承自HP版 本。被GCC(Linux)采用,可移植性好, 可公開、修改甚至販賣,從命名風格和編程 風格上看,閱讀性非常高。我們后面學習STL要閱讀部分源代碼, 主要參考的就是這個版本。

STL的缺陷

1.STL庫的更新太慢了。這個得嚴重吐槽,上一版靠譜是C++98,中間的C++03基本一些修訂。C++11出 來已經相隔了13年,STL才進一步更新。

2. STL現在都沒有支持線程安全。并發環境下需要我們自己加鎖。且鎖的粒度是比較大的。

3. STL極度的追求效率,導致內部比較復雜。比如類型萃取,迭代器萃取。

4. STL的使用會有代碼膨脹的問題,比如使用vector/vector/vector這樣會生成多份代碼,當然這是模板語 法本身導致的。

標準庫中的string類 了解string類

1. string是表示字符串的字符串類

2. 該類的接口與常規容器的接口基本相同,再添加了一些專門用來操作string的常規操作。

3. string在底層實際是:basic_string模板類的別名,typedef basic_string string;

4. 不能操作多字節或者變長字符的序列。

在使用string類時,必須包含#include頭文件以及using namespace std;

string類的常用接口說明

在標準庫中string有7個接口,這里我們主要講解和使用4個,后面模擬實現也是模擬我們常用的。

default (1)
string();
copy (2)
string (const string& str);
substring (3)
string (const string& str, size_t pos, size_t len = npos);
from c-string (4)
string (const char* s);
from sequence (5)
string (const char* s, size_t n);
fill (6)
string (size_t n, char c);
range (7)
templatestring  (InputIterator first, InputIterator last);

string()

Constructs an?empty?string, with a?length?of zero characters

----構造一個長度為零個字符的空字符串。

#include#includeusing namespace std;

int main()
{
	string str;//構造空的string類對象s1

	cout<< str<< endl;

	return 0;
}

string(const char* s)?

Copies the null-terminated character sequence (C-string) pointed by?s.

----復制s指向的以空字符結尾的字符序列(C字符串)

#include#includeusing namespace std;

int main()
{
	string str("hello world"); 

	cout<< str<< endl;

	return 0;
}

string(size_t n, char c)?

Fills the string with?nconsecutive copies of character?c

----用字符c的n個連續副本填充字符串

#include#includeusing namespace std;

int main()
{
	string str(12,'x'); 

	cout<< str<< endl;

	return 0;
}

string(const string&s)

Constructs a copy of?str.

----構造str的副本。

#include#includeusing namespace std;

int main()
{

	string str("hello world");
	string str1(str);

	cout<< str1<< endl;

	return 0;
}
string類對象的容量操作

size

size_t size() const;

Return length of string--返回字符串有效字符長度

注意:

Returns the length of the string, in terms of bytes.

This is the number of actual bytes that conform the contents of the?string, which is not necessarily equal to its storage?capacity.

Note that?string?objects handle bytes without knowledge of the encoding that may eventually be used to encode the characters it contains. Therefore, the value returned may not correspond to the actual number of encoded characters in sequences of multi-byte or variable-length characters (such as UTF-8).

----返回字符串的長度,以字節為單位。這是符合字符串內容的實際字節數,不一定等于其存儲容量。

請注意,字符串對象在不知道最終可能用于編碼其包含的字符的編碼的情況下處理字節。

因此,返回的值可能不對應于多字節或可變長度字符序列(如UTF-8)中編碼字符的實際數量

這里的更深層的理解就是:我們都是熟知ASCLL表,我們打印的字符,或者符號都是被數字所表示的。因為計算機是只認識二進制,所以這些字符或者符號都會變成二進制讓計算機識別。世界上有各種語言,所以對應的就有不同的電腦編碼。英文對應的電腦編碼是ASCLL,中文對應的電腦編碼很多時候就是統一碼

統一碼(Unicode),也叫萬國碼、單一碼,由統一碼聯盟開發,是計算機科學領域里的一項業界標準,包括字符集、編碼方案等。統一碼是為了解決傳統的字符編碼方案的局限而產生的,它為每種語言中的每個字符設定了統一并且唯一的二進制編碼,以滿足跨語言、跨平臺進行文本轉換、處理的要求。

統一碼是國際組織制定的可以容納世界上所有文字和符號的字符編碼方案。統一碼用數字0-0x10FFFF來映射這些字符,最多可以容納1114112個字符,或者說有1114112個碼位。碼位就是可以分配給字符的數字。UTF-8、UTF-16、UTF-32都是將數字轉換到程序數據的編碼方案。分別映射為8位,16位,32位長的整數。

#include#includeusing namespace std;

int main()
{
	string str("hello world");
	size_t n=str.size();

	cout<< n<< endl;

	return 0;
}

length

size_t length() const;

Return length of string ----返回字符串的長度

#include#includeint main ()
{
  std::string str ("Test string");
  std::cout<< "The size of str is "<< str.length()<< " bytes.\n";
  return 0;
}

注意:

length與size的實現時一樣的,只是命名不一樣,這個是歷史遺留問題,就不用過多的吐槽,在后面容器中就再也沒有length了,只在string中有。

capacity

size_t capacity() const;

Return size of allocated storage----返回空間總大小

#include#includeint main ()
{
  std::string str ("Test string");
  std::cout<< "The size of str is "<< str.length()<< " bytes.\n";
  return 0;
}

empty

bool empty() const;

Test if string is empty----檢測字符串釋放為空串,是返回true,否則返回false

#include#includeusing namespace std;

int main()
{
	string str("hello world");
	
	if (!str.empty())
	{
		cout<< "hello everyone!"<< endl;
	}
	return 0;
}

clear

void clear();

Clear string----清空有效字符

#include#includeusing namespace std;

int main()
{
	string str("hello world");
	
	str.clear();
	cout<< str<< endl;

	return 0;
}

注意:

clear是清空有效字符但是不清除內存,因為一般情況是下是內存足夠,缺時間。一般會選擇增容不會減少內存,增加內存會重新開辟一塊內存,因為內存中存在這內存碎片。減少內存之后,重新寫入就會增加空間,那么就更加耗時,所以一般情況下是不減少內存的。

reserve

void reserve (size_t n = 0);

Request a change in capacity-----請求更改容量

Requests that the?string capacity?be adapted to a planned change in?size?to a?length?of up to?ncharacters.----請求字符串容量根據計劃的大小更改調整為最多n個字符的長度

If?nis greater than the current?string capacity, the function causes the container to increase its?capacity?to?ncharacters (or greater).

In all other cases, it is taken as a non-binding request to shrink the?string capacity: the container implementation is free to optimize otherwise and leave the?string?with a?capacity?greater than?n.

This function has no effect on the?string length?and cannot alter its content.

----如果n大于當前字符串容量,則該函數使容器將其容量增加到n個字符(或更大)。在所有其他情況下,它被視為縮小字符串容量的非綁定請求:容器實現可以自由優化,否則將字符串容量保留為大于n。此函數對字符串長度沒有影響,也不能更改其內容。

vs2013

#include#includeusing namespace std;

void TestPushBack()
{
	string s;
	s.reserve(500);
	size_t sz = s.capacity();
	cout<< "capacity changed: "<< sz<< '\n';
	cout<< s.size()<< endl;
	cout<< "making s grow:\n";
	for (int i = 0; i< 1000; ++i)
	{
		s.push_back('c');
		if (sz != s.capacity())
		{
			sz = s.capacity();
			cout<< "capacity changed: "<< sz<< '\n';
		}
	}
}

int main()
{
	TestPushBack();

	return 0;
}

我們發現在vs2013環境下,它是以1.5倍擴容的

linux下--同樣的代碼

那么在linux下我們發現,它是以2倍擴容的

我們知道要插入多少數據,提前用reserve開好空間,避免擴容,提高效率

resize

void resize (size_t n);

void resize (size_t n, char c);

Resize string----調整字符串大小

Resizes the string to a?length?of?ncharacters.

If?nis smaller than the current?string length, the current value is shortened to its first?ncharacter, removing the characters beyond the?nth.

If?nis greater than the current?string length, the current content is extended by inserting at the end as many characters as needed to reach a size of?n. If?cis specified, the new elements are initialized as copies of?c, otherwise, they are?value-initialized characters(null characters).

-----將字符串的長度調整為n個字符。如果n小于當前字符串長度,則將當前值縮短為其第一個n個字符,刪除第n個字符以外的字符。如果n大于當前字符串長度,則通過在末尾插入盡可能多的字符來擴展當前內容,以達到n的大小。如果指定了c,則將新元素初始化為c的副本,否則,它們是值初始化字符(空字符)。

#include#includeint main()
{
	std::string str("I like to code in C");
	std::cout<< str<< '\n';

	unsigned sz = str.size();
	std::cout<< sz<< '\n';

	str.resize(sz + 2, '+');//增加兩個字符‘++’
	std::cout<< str<< '\n';

	str.resize(14);//減少5個字符
	std::cout<< str<< '\n';
	return 0;
}

string類對象的訪問及遍歷操作

operator[]

char& operator[] (size_t pos);

const char& operator[] (size_t pos) const;

Returns a reference to the character at position?posin the?string.

----返回pos位置的字符,const string類對象調用

#include#includeint main()
{
	std::string str("Test string");
	for (int i = 0; i

begin /end

iterator begin();

const_iterator begin() const;

Returns an iterator pointing to the first character of the string

----返回指向字符串第一個字符的迭代器?

iterator end();

const_iterator end() const;

Returns an iterator pointing to the?past-the-endcharacter of the string.

----返回一個迭代器,該迭代器指向字符串的結束字符。

#include#includeint main()
{
	std::string str("Test string");
	for (std::string::iterator it = str.begin(); it != str.end(); ++it)
		std::cout<< *it;
	std::cout<< '\n';

	return 0;
}

rend/rbegin

reverse_iterator rbegin();
const_reverse_iterator rbegin() const;

Return reverse iterator to reverse beginning

----返回反向迭代器以反向開始

reverse_iterator rend();
const_reverse_iterator rend() const;

Return reverse iterator to reverse end

----將反向迭代器返回到反向端

#include#includeint main ()
{
  std::string str ("now step live...");
  for (std::string::reverse_iterator rit=str.rbegin(); rit!=str.rend(); ++rit)
    std::cout<< *rit;
  return 0;
}

C++中,迭代器就是一個類似于指針的對象,它能夠用來遍歷C++標準模板庫容器中的部分或全部元素,每個迭代器對象代表容器中的確定的地址。

這里需要注意的是,,對于string類來說,無論是正向遍歷,還是反向遍歷,下標+[]都足夠好用,但是對于其他容器,對于那些以鏈表形式連接的數據結構,如list,map/set等,就不能使用下標+[]的方式去訪問容器里的元素,所以就需要采用迭代器來訪問這些容器里的元素。

vector--容器

vectorv;
	vector::iterator vit = v.begin();
	while (vit != v.end())
	{
		cout<< *vit<< " ";
		vit++;
	}
	cout<< endl;
string類對象的修改操作

push_back

void push_back (char c);

ppends character?cto the end of the?string, increasing its?length?by one.

----將字符c追加到字符串的末尾,將其長度增加1

#include#include#includeusing namespace std;

int main()
{
	string str;
	str.push_back('h');
	str.push_back('e');
	str.push_back('l');
	str.push_back('l');
	str.push_back('o');

	std::cout<< str<< '\n';
	return 0;
}

append

string (1)
string& append (const string& str);
substring (2)
string& append (const string& str, size_t subpos, size_t sublen);
c-string (3)
string& append (const char* s);
buffer (4)
string& append (const char* s, size_t n);
fill (5)
string& append (size_t n, char c);
range (6)
templatestring& append (InputIterator first, InputIterator last);

(1) string

Appends a copy of?str.----追加str的副本

(2) substring

Appends a copy of a substring of?str. The substring is the portion of?strthat begins at the character position?subposand spans?sublencharacters (or until the end of?str, if either?stris too short or if?sublenis?string::npos).

----追加str的子字符串的副本。該子字符串是str的一部分,從字符位置子字符串開始,跨越子字符串字符(或直到str結尾,如果str太短或子字符串為string::npos)

(3) c-string

Appends a copy of the string formed by the null-terminated character sequence (C-string) pointed by?s.

----追加由s指向的以空結尾的字符序列(C字符串)形成的字符串的副本

(4) buffer

Appends a copy of the first?ncharacters in the array of characters pointed by?s.

----追加由s指向的字符數組中前n個字符的副本

(5) fill

Appends?nconsecutive copies of character?c.

----追加n個字符c的連續副本。

(6) range

Appends a copy of the sequence of characters in the range?[first,last), in the same order.

----以相同的順序追加范圍[first,last]中字符序列的副本。

#include#includeint main()
{
	std::string str;
	std::string str2 = "Writing ";
	std::string str3 = "print 10 and then 5 more";

	
	str.append(str2);                       // 情況1
	std::cout<< str<< '\n';

	str.append(str3, 6, 3);                   // 情況2
	std::cout<< str<< '\n';

	str.append("dots are cool", 5);          // 情況3
	std::cout<< str<< '\n';

	str.append("here: ");                   // 情況4
	std::cout<< str<< '\n';

	str.append(10u, '.');                    // 情況5
	std::cout<< str<< '\n';

	str.append(str3.begin() + 8, str3.end());  // 情況6
	std::cout<< str<< '\n';

	return 0;
}

operator+=

Append to string----附加到字符串

string (1)
string& operator+= (const string& str);
c-string (2)
string& operator+= (const char* s);
character (3)
string& operator+= (char c);
#include#includeint main()
{
	std::string name("John");
	std::string family("Smith");
	name += " K. ";         // c-string
	name += family;         // string
	name += '\n';           // character

	std::cout<< name;
	return 0;
}

運行結果:John K. Smith

c_str

const char* c_str() const;

Get C string equivalent----獲取等效的C字符串

Returns a pointer to an array that contains a null-terminated sequence of characters (i.e., a C-string) representing the current value of the string object.

This array includes the same sequence of characters that make up the value of the string object plus an additional terminating null-character ('\0') at the end.

----返回指向數組的指針,該數組包含表示字符串對象當前值的以空結尾的字符序列(即C字符串)。

此數組包含構成字符串對象值的相同字符序列,加上末尾的附加終止空字符(“\0”)。

#include#include#includeint main()
{
	std::string str("Please split this sentence into tokens");//str是一個類

	char * cstr = new char[str.length() + 1];
	std::strcpy(cstr, str.c_str());				//將字符串返回成char *,然后進行拷貝
												//char *strcpy(char *dest, const char *src)
												//  str現在包含str的c字符串副本

	char * p = std::strtok(cstr, " ");			//char *strtok( char *strToken, const char *strDelimit );
												//分解字符串為一組字符串

	while (p != 0)
	{
		std::cout<< p<< '\n';
		p = std::strtok(NULL, " ");
	}

	delete[] cstr;
	return 0;
}

find?+ npos

string (1)
size_t find (const string& str, size_t pos = 0) const;
c-string (2)
size_t find (const char* s, size_t pos = 0) const;
buffer (3)
size_t find (const char* s, size_t pos, size_t n) const;
character (4)
size_t find (char c, size_t pos = 0) const;

Find content in string----在字符串中查找內容

static const size_t npos = -1;

npos:Maximum value for size_t----size_t的大值

#include  
#includeusing namespace std;

int main()
{
	string str("There are two needles in this haystack with needles.");
	string str2("needle");

	// different member versions of find in the same order as above:
	size_t found = str.find(str2);
	if (found != string::npos)
		cout<< "first 'needle' found at: "<< found<< '\n';

	found = str.find("needles are small", found + 1, 6);
	if (found != string::npos)
		cout<< "second 'needle' found at: "<< found<< '\n';

	found = str.find("haystack");
	if (found != string::npos)
		cout<< "'haystack' also found at: "<< found<< '\n';

	found = str.find('.');
	if (found != string::npos)
		cout<< "Period found at: "<< found<< '\n';

	// let's replace the first needle:
	str.replace(str.find(str2), str2.length(), "preposition");
	cout<< str<< '\n';

	return 0;
}

r?nd ?

string (1)
size_t rfind (const string& str, size_t pos = npos) const;
c-string (2)
size_t rfind (const char* s, size_t pos = npos) const;
buffer (3)
size_t rfind (const char* s, size_t pos, size_t n) const;
character (4)
size_t rfind (char c, size_t pos = npos) const;

Find last occurrence of content in string----查找字符串中內容的最后一次出現

#include#include#includeint main()
{
	std::string str("The sixth sick sheik's sixth sheep's sick.");
	std::string key("sixth");

	std::size_t found = str.rfind(key);//找到sixth第二次出現的位置

	if (found != std::string::npos)
		str.replace(found, key.length(), "seventh");//seventh替換掉二次出現的sixth

	std::cout<< str<< '\n';

	return 0;
}

輸出結果:The sixth sick sheik's seventh sheep's sick.

substr

string substr (size_t pos = 0, size_t len = npos) const;

Returns a newly constructed?string?object with its value initialized to a copy of a substring of this object-----返回一個新構造的字符串對象,其值初始化為此對象的子字符串的副本。

#include#includeint main ()
{
  std::string str="We think in generalities, but we live in details.";
                                           // (quoting Alfred N. Whitehead)

  std::string str2 = str.substr (3,5);     // "think"

  std::size_t pos = str.find("live");      // position of "live" in str

  std::string str3 = str.substr (pos);     // get from "live" to the end

  std::cout<< str2<< ' '<< str3<< '\n';

  return 0;
}

注意:

1. 在string尾部追加字符時,s.push_back(c) / s.append(1, c) / s += 'c'三種的實現方式差不多,一般 情況下string類的+=操作用的比較多,+=操作不僅可以連接單個字符,還可以連接字符串。

2. 對string操作時,如果能夠大概預估到放多少字符,可以先通過reserve把空間預留好

string類非成員函數

operator+ (string)

string (1)
string operator+ (const string& lhs, const string& rhs);
c-string (2)
string operator+ (const string& lhs, const char*   rhs);
string operator+ (const char*   lhs, const string& rhs);
character (3)
string operator+ (const string& lhs, char          rhs);
string operator+ (char          lhs, const string& rhs);

Concatenate strings?----連接字符串

#include#includeint main()
{
	std::string firstlevel("com");
	std::string secondlevel("cplusplus");
	std::string scheme("http://");
	std::string hostname;
	std::string url;

	hostname = "www. " + secondlevel + '.' + firstlevel;
	url = scheme + hostname;

	std::cout<< url<< '\n';

	return 0;
}

運行結果:http://www. cplusplus.com

operator>>(string)?

istream& operator>>(istream& is, string& str);

Extract string from stream----從流中提取字符串

#include#includeint main()
{
	std::string name;

	std::cout<< "Please, enter your name: ";
	std::cin >>name;
	std::cout<< "Hello, "<< name<< "!\n";

	return 0;
}

operator<< (string)

ostream& operator<< (ostream& os, const string& str);

Insert string into stream----將字符串插入流?

#include#includeint main()
{
	std::string str = "Hello world!";
	std::cout<< str<< '\n';
	return 0;
}

getline (string)

(1)
istream& getline (istream& is, string& str, char delim);
(2)
istream& getline (istream& is, string& str);

Get line from stream into string----從流獲取線到字符串?

#include#includeint main()
{
	std::string name;

	std::cout<< "Please, enter your full name: ";
	std::getline(std::cin, name);
	std::cout<< "Hello, "<< name<< "!\n";

	return 0;
}

relational operators (string)

(1)
bool operator== (const string& lhs, const string& rhs);
bool operator== (const char*   lhs, const string& rhs);
bool operator== (const string& lhs, const char*   rhs);
(2)
bool operator!= (const string& lhs, const string& rhs);
bool operator!= (const char*   lhs, const string& rhs);
bool operator!= (const string& lhs, const char*   rhs);
(3)
bool operator<  (const string& lhs, const string& rhs);
bool operator<  (const char*   lhs, const string& rhs);
bool operator<  (const string& lhs, const char*   rhs);
(4)
bool operator<= (const string& lhs, const string& rhs);
bool operator<= (const char*   lhs, const string& rhs);
bool operator<= (const string& lhs, const char*   rhs);
(5)
bool operator>(const string& lhs, const string& rhs);
bool operator>(const char*   lhs, const string& rhs);
bool operator>(const string& lhs, const char*   rhs);
(6)
bool operator>= (const string& lhs, const string& rhs);
bool operator>= (const char*   lhs, const string& rhs);
bool operator>= (const string& lhs, const char*   rhs);

Relational operators for string----字符串的關系運算符

#include#includeint main()
{
	std::string foo = "alpha";
	std::string bar = "beta";

	if (foo == bar) std::cout<< "foo and bar are equal\n";
	if (foo != bar) std::cout<< "foo and bar are not equal\n";
	if (foo< bar) std::cout<< "foo is less than bar\n";
	if (foo>bar) std::cout<< "foo is greater than bar\n";
	if (foo<= bar) std::cout<< "foo is less than or equal to bar\n";
	if (foo >= bar) std::cout<< "foo is greater than or equal to bar\n";

	return 0;
}

string類的模擬實現 經典的string類問題

我們在模擬實現string的時候,可以先看看string類的經典問題。在實現string類的構造、拷貝構造、賦值運算符重載以及析構函數時,經常會出現深淺拷貝的問題。

構造String類對象的錯誤:

String(const char* str = "\0") ----錯誤示范,"\0"是需要內存存放的

String(const char* str = nullptr) ----錯誤示范,String是類,地址是不會指向nullptr

Sring(const char* str = "")----正確示范

構造String類對象時,如果傳遞nullptr指針,可以認為程序非法。

淺拷貝的問題:

#include#include 
using namespace std;
class String
{
public:
String(const char* str = "")
{
	
	if (nullptr == str)
	{
		assert(false);
		return;
	}

	_str = new char[strlen(str) + 1];
	strcpy(_str, str);
}

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

private:
	char* _str;
};
// 測試
void TestString()
{
	String s1("hello bit!!!");
	String s2(s1);
}

int main()
{
	TestString();

	return 0;
}

在這段代碼中,我們發現測試的時候,開始s1傳入字符串是沒有問題的,但是將s1的地址傳入s2時,就會出現錯誤。因為在s2調用構造函數的時候,s2中的_str存放的地址也是s1的地址。那么在調用析構函數的時候,我們就會發現調用了兩次析構函數,第一將_str清除后,s2再調用析構函數的時候,此時_str已經是隨機值,再對_str進行刪除的時候就會報錯。

運行結果:

說明:上述String類沒有顯式定義其拷貝構造函數與賦值運算符重載,此時編譯器會合成默認的,當用s1構造s2時,編譯器會調用默認的拷貝構造。最終導致的問題是,s1、s2共用同一塊內存空間,在釋放時同一塊 空間被釋放多次而引起程序崩潰,這種拷貝方式,稱為淺拷貝。

那么什么是深拷貝?什么是淺拷貝呢?

淺拷貝/深拷貝

淺拷貝:也稱位拷貝,編譯器只是將對象中的值拷貝過來。如果對象中管理資源,最后就會導致多個對象共享同一份資源,當一個對象銷毀時就會將該資源釋放掉,而此時另一些對象不知道該資源已經被釋放,以為還有效,所以當繼續對資源進項操作時,就會發生發生了訪問違規。

可以采用深拷貝解決淺拷貝問題,即:每個對象都有一份獨立的資源,不要和其他對象共享。

深拷貝:如果一個類中涉及到資源的管理,其拷貝構造函數、賦值運算符重載以及析構函數必須要顯式給出。一般情況都是按照深拷貝方式提供。

string類的模擬實現
#define _CRT_SECURE_NO_WARNINGS

#includeusing namespace std;
#include 

#define npos -1

namespace bit
{
	class string
	{
	public:
		typedef char* iterator;

	public:
		string(const char* str = "")//構造空的string類對象,即空字符串
		{
			// 構造String類對象時,如果傳遞nullptr指針,可以認為程序非法
			if (nullptr == str)
			{
				assert(false);
				return;
			}

			_str = new char[strlen(str) + 1];//擴容要加'/0'
			strcpy(_str, str);
		}

		string(const string& s) //拷貝構造函數
			: _str(new char[strlen(s._str) + 1])//初始化列表
		{
			strcpy(_str, s._str);
		}

		string& operator=(const string& s)//字符串賦值
		{
			if (this != &s)
			{
				char* pStr = new char[strlen(s._str) + 1];
				strcpy(pStr, s._str);
				delete[] _str;
				_str = pStr;
			}
			return *this;
		}

		~string()//析構函數
		{
			if (_str)
			{
				delete[] _str;
				_str=nullptr;
			}
		}
		/
		// iterator--迭代器
		iterator begin()//返回到開始
		{
			return _str;
		}

		iterator end()//返回到結束
		{
			return _str + _size;
		}

		/
		// modify--修改

		void push_back(char c)//將字符附加到字符串
		{
			if (_size == _capacity)
				reserve(_capacity * 2);//2倍擴容

			_str[_size++] = c;
			_str[_size] = '\0';//++后添加上‘\0’
		}

		string& operator+=(char c)
		{
			push_back(c);
			return *this;
		}

		
		void append(const char* str)
		{
			size_t n = strlen(str);
			if (_size + n >_capacity )
			{
				reserve(_size + n);
			}

			strcpy(_str + _size, str);
			_size += n;

		}
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}

		void swap(string& s)
		{
			std::swap(_str,s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);

		}

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

		/
		// capacity
		size_t size()const
		{
			return _size;
		}
		size_t capacity()const
		{
			return _capacity;
		}

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

		void resize(size_t newSize, char c = '\0')
		{
			if (newSize >_size)
			{
				// 如果newSize大于底層空間大小,則需要重新開辟空間
				if (newSize >_capacity)
				{
					reserve(newSize);
				}

				memset(_str + _size, c, newSize - _size);
			}

			_size = newSize;
			_str[newSize] = '\0';
		}

		void reserve(size_t newCapacity)
		{
			// 如果新容量大于舊容量,則開辟空間
			if (newCapacity >_capacity)
			{
				char* str = new char[newCapacity + 1];
				strcpy(str, _str);

				// 釋放原來舊空間,然后使用新空間
				delete[] _str;
				_str = str;
				_capacity = newCapacity;
			}
		}

		
		// access
		char& operator[](size_t index)
		{
			assert(index< _size);
			return _str[index];
		}

		const char& operator[](size_t index)const
		{
			assert(index< _size);
			return _str[index];
		}

		
		// 作業
		bool operator<(const string& s)
		{
			return strcmp(c_str(), s.c_str())<0;//需要接受char*
		}
		bool operator<=(const string& s)
		{
			return *this< s || *this == s;
		}
		//復用前<  ==
		bool operator>(const string& s)
		{
			return !(*this<= s);
		}

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

		bool operator==(const string& s)
		{
			return strcmp(c_str(), s.c_str()) == 0;
		}

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

		// 返回c在string中第一次出現的位置

		size_t find(char c, size_t pos = 0) const
		{
			assert(pos< _size);
			while (pos< _size)
			{
				if (_str[pos] == c)
				{
					return pos;
				}
				pos++;
			}
			return npos;
		}

		// 返回子串s在string中第一次出現的位置
		//strstr:返回字符串中首次出現子串的地址
		size_t find(const char* s, size_t pos = 0) const
		{
			assert(pos< _size);
			const char* ptr = strstr(_str + pos, _str);

			if (ptr == nullptr)
			{
				return npos;
			}
			else
			{
				return ptr - _str;
			}
		}

		// 在pos位置上插入字符c/字符串str,并返回該字符的位置
		string& insert(size_t pos, char c)
		{
			assert(pos<= _size);
			if (_size == _capacity)
			{
				size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;
				reserve(newCapacity);
			}

			//end--后,當減到-1時,是size_t類型就會變成一個很大的數,故此必須寫成int類型,pos也將被強轉
			//int end = _size;
			//while (end >= (int)pos)
			//{
			//	_str[end + 1] = _str[end];
			//	end--;
			//}
			size_t end = _size + 1;
			while (end >pos)
			{
				_str[end] = _str[end - 1];
				end--;
			}

			_str[pos] = c;
			++_size;
		}

		string& insert(size_t pos, const char* str)
		{
			assert(pos<= _size);
			size_t len = strlen(str);
			if (_size == _capacity)
			{
				size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;
				reserve(newCapacity);
			}

			size_t end = _size + len;

			while (end >pos + len - 1)
			{
				_str[end] = _str[end - len];
				end--;
			}

			strncpy(_str + len, str, len);
			return *this;
		}

		// 刪除pos位置上的元素,并返回該元素的下一個位置
		string& erase(size_t pos, size_t len)
		{
			assert(pos< _size);

			if (len == npos || pos + len >= _size - pos)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
			return *this;
		}

	private:
		friend ostream& operator<<(ostream& _cout, const bit::string& s);
		friend istream& operator>>(istream& _cin, bit::string& s);

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

	ostream& operator<<(ostream& _cout, const bit::string& s)
	{
		// 不能使用這個, 因為string的字符串內部可能會包含\0
		// 直接cout時, 是將_str當成char*打印的,遇到內部的\0時后序內容就不打印了
		//cout<< s._str;
		for (size_t i = 0; i< s.size(); ++i)
		{
			_cout<< s[i];
		}
		return _cout;
	}
}

你是否還在尋找穩定的海外服務器提供商?創新互聯www.cdcxhl.cn海外機房具備T級流量清洗系統配攻擊溯源,準確流量調度確保服務器高可用性,企業級服務器適合批量采購,新人活動首月15元起,快前往官網查看詳情吧

分享文章:【c++】STL--string-創新互聯
文章轉載:http://www.yijiale78.com/article42/ddcchc.html

成都網站建設公司_創新互聯,為您提供云服務器動態網站網站制作外貿建站Google域名注冊

廣告

聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯