STL序列式容器 - string

string类中的常用接口

#include<string>
  1. 构造与析构
// 构造
string();

string(const string& str);
string(const string& str, size_t pos, size_t len = npos);

string(const char* s);
string(const char* s, size_t n);

string(size_t n, char c);

template<class InputIterator>
string(InputInterator first, InputIterator last);

// 析构
~string();

例一:构造函数的使用

void Test1()
{
	string s1; // 构造一个空字符串 里面有一个\0
	string s2("hello"); // 用C串构造string对象
	string s3("world", 4);  // 用C串的前4个字符构造
	string s4(10, 'a'); // 用10个字符a构造
	string s5(s2); // 拷贝构造
	string s6(s2, 0, 3); // 用s2对象从0号位置开始的3个字符构造
	string s7(s2.begin(), s2.begin() + 3); // 区间构造 左闭右开	
}
  1. 容量相关操作
size_t size()const;
size_t length()const;

void resize(size_t n);
void resize(size_t n, char c);

size_t capacity()const;

void reserve(size_t n = 0);

void clear();

bool empty()const;
  1. 元素访问
char& operator[](size_t pos);
const char& operator[](size_t pos);

char& back();
const char& back()const;

char& front();
const char& front()const;

char& at(size_t pos);
const char& at(size_t pos)const;
  1. 迭代器
iterator begin();
const iterator begin()const;

iterator end();
const iterator end()const;

reverse_iterator rbegin();
const_reverse_iterator rbegin()const;

reverse_iterator rend();
const_reverse_iterator rend()const;
  1. 修改
// 增加

// 1. operator+=
string& operator+=(const string& str);
string& operator+=(const char* s);
string& operator+=(char c);

// 2. append
string& append(const string& str);
string& append(const string& str, size_t subpos, size_t sublen);

string& append(const char* s);
string& append(const char* s, size_t n);

string& append(size_t n, char c);

template<class InputIterator>
string& append(InputIterator first, InputIterator last);

// 3. push_back
void push_back(char c);

// 4. insert
string& insert(size_t pos, const string& str);
string& insert(size_t pos, const string& str, size_t subpos, size_t sublen);

string& insert(size_t pos, const char* s);
string& insert(size_t pos, const char* s, size_t n);

string& insert(size_t pos, size_t n, char c);

void insert(iterator p, size_t n, char c);
iterator insert(iterator p, char c);

template <class InputIterator>
void insert(iterator p, InputIterator first, InputIterator last);
// 删除

// 1. erase
string& erase(size_t pos = 0, size_t len = npos);
iterator erase(iterator p);
iterator erase(iterator first, iterator last);

// 2. pop_back
void pop_back();
// assign:用新字符串替换旧字符串
string& assign(const string& str);
string& assign(const string& str, size_t subpos, size_t sublen);

string& assign(const char* s);
string& assign(const char* s, size_t n);

string& assign(size_t n, char c);

template <class InputIterator>
string& assign(InputIterator first, InputIterator last);

// replace:替换字符串的一部分
string& replace(size_t pos,  size_t len,  const string& str);
string& replace(iterator i1, iterator i2, const string& str);
string& replace(size_t pos,  size_t len,  const string& str, size_t subpos, size_t sublen);

string& replace(size_t pos,  size_t len,  const char* s);
string& replace(iterator i1, iterator i2, const char* s);
string& replace(size_t pos,  size_t len,  const char* s, size_t n);
string& replace(iterator i1, iterator i2, const char* s, size_t n);

string& replace(size_t pos,  size_t len,  size_t n, char c);
string& replace(iterator i1, iterator i2, size_t n, char c);

template <class InputIterator>
string& replace (iterator i1, iterator i2, InputIterator first, InputIterator last);
void swap(string& str);
  1. 特殊操作
const char* c_str()const;

size_t find(const string& str, size_t pos = 0)const;
size_t find(const char* s, size_t pos = 0)const;
size_t find(const char* s, size_t pos, size_t n)const;
size_t find(char c, size_t pos = 0)const;

size_t rfind(const string& str, size_t pos = npos) const;
size_t rfind(const char* s, size_t pos = npos) const;
size_t rfind(const char* s, size_t pos, size_t n) const;
size_t rfind(char c, size_t pos = npos) const;

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

int compare(const string& str) const;
int compare(size_t pos, size_t len, const string& str) const;
int compare(size_t pos, size_t len, const string& str,size_t subpos, size_t sublen) const;
int compare(const char* s) const;
int compare(size_t pos, size_t len, const char* s) const;
int compare(size_t pos, size_t len, const char* s, size_t n) const;
  1. 成员常量
static const size_t npos = -1;
  1. 非成员函数重载
istream& getline(istream& is, string& str, char delim);
istream& getline(istream& is, string& str);

// std::swap(string)
void swap(string& x, string& y);

元素遍历方式

// 法一:
for(int i = 0; i < str.size(); i++
{
	cout << str[i] << " ";
}
cout << endl;

// 法二:
for(auto e : str)
	cout << e << " ";
cout << endl;

// 法三:
string::iterator it = str.begin();
while(it != str.end())
{
	cout << *it << " ";
	++it;
}
cout << endl;

深度理解resize()

假设resize前string对象的_size为oldsize,_capacity为oldcapacity,resize后string对象的_size为oldsize,_capacity为newcapacity

  1. newsize <= oldsize
    将有效元素个数减少到newsize个,_capacity不改变
  2. newsize > oldsize
    1)newsize <= oldcapacity :在原空间后加
    如果是void resize(size_t n);,在原字符串后追加 (newsize - oldsize)个0;
    如果是void resize(size_t n, char c);,在原字符串后追加 (newsize - oldsize)个字符c
    2)newsize > oldcapacity
    要进行以下操作:
    ① 开辟新空间
    ② 将旧空间元素拷贝到新空间
    ③ 释放旧空间
    ④ 让_str指向新空间
    ⑤ _capacity = newsize
    ⑥ 在新空间后加:如果是void resize(size_t n);,在原字符串后追加 (newsize - oldsize)个0;如果是void resize(size_t n, char c);,在原字符串后追加 (newsize - oldsize)个字符c

深度理解reserve()

  1. newcapacity <= oldcapacity
    1)newcpacity <= 15
    _capacity 最终变为15
    2)newcpacity > 15
    reserve直接忽略
  2. newcapacity > oldcapacity
    vs:1.5倍 Linux:2倍

std::swap和std::string::swap的区别

void Test1()
{
	string s1("hello world");
	string s2("hahahah");

	//重新开辟空间,交换s1,s2的值
	std::swap(s1, s2);

	//交换两个指针指向的不同空间
	s1.swap(s2);
}

迭代器失效场景

  • 所有可能引起容量修改的操作
    push_back / insert / operator+= / append
    resize() / reserve()
    其他:erase / clear / swap

  • 解决迭代器失效的办法:在可能会引起迭代器失效的操作之后对迭代器重新赋值

浅拷贝和深拷贝

  • 当一个类中没有显式给出拷贝构造函数或者赋值运算符重载,编译器会生成默认的拷贝构造函数以及赋值运算符重载,但是它们都是浅拷贝

  • 浅拷贝:将一个对象中的内容按字节拷贝至另一个对象,两个对象使用同一份资源
    浅拷贝存在的问题:当类中未涉及到资源管理时,不会出现任何问题;但是当类中涉及到资源管理时,如果用户不显式定义拷贝构造函数或赋值运算符重载函数,当多个对象销毁时,会将同一份资源释放多次而引起代码崩溃

  • 浅拷贝解决方式
    1)深拷贝:即每个对象各自拥有一份资源
    2)写时拷贝:浅拷贝+引用计数
    引用计数:记录该份资源被多少个对象在使用,保证了多个对象销毁时资源只释放一次

传统版:

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

#if 0
		if (str != nullptr)
		{
			_str = new char[strlen(str) + 1];
			strcpy(_str, str); // 拷贝包括\0
		}
		else // 如果传参为nullptr 将其当成空字符串处理
		{
			// 这种写法,构造string对象时用的是new,析构函数释放时要用delete
			// 实现起来不方便
			_str = new char;
			*_str = '\0';
		}
#endif		
	}

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

	/*
	编译器默认生成的拷贝构造函数是浅拷贝
	浅拷贝:两个对象指向同一块空间
	浅拷贝在释放资源时,会将同一块空间释放两次导致程序崩溃
	所以,涉及到资源管理要显式定义拷贝构造函数
	深拷贝:两个对象各自拥有一块空间
	*/
	String(const String& s)
		:_str(new char[strlen(s._str)+1])
	{ 
		strcpy(_str, s._str);
	}

	/*
	编译器默认生成的赋值运算符重载存在两个问题:
	一是浅拷贝,二是被赋值对象原来的空间被覆盖,导致没有释放,存在内存泄漏
	赋值运算符重载同拷贝构造一样,涉及到资源管理,一定要显式定义
	注意:如果被赋值空间 < 赋值对象空间 那么strcpy会出错
	      所以,先开辟一段和赋值对象空间一样大的临时空间,然后将赋值对象中的内容拷贝到临时空间
		  然后释放赋值对象的旧空间,让其指针指向临时空间
	*/
	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;
	}


private:
	char* _str;
};

现代版:

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

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

	}

	/* 
	该拷贝构造如果不写初始化列表会出问题
	Test2没问题,但是Test3释放资源会出问题
	*/
	String(const String& s)
		:_str(nullptr)
	{
		// 用构造函数构造一个临时变量
		String sTemp(s._str);
		// 交换this指针指向对象的_str和临时对象的_str的地址
		std::swap(_str,sTemp._str);
	}


	// 因为赋值时,自己给自己赋值的情况很少,所以采用以下做法
	/*
	这种写法的优势,巧妙的使用了传值构造的临时对象
	传参时,对象传参要调用拷贝构造函数先创建一份临时变量
	将临时变量的_str和this指针指向对象的_str的地址交换
	临时对象在函数执行完要销毁,调用析构函数,将被赋值对象原来的空间释放掉
	*/
	String& operator=(String s)
	{
		std::swap(_str, s._str);
		return *this;
	}
	
	// 下面代码依然重复度高
	/*String& operator=(const String& s)
	{
		if (this != &s)
		{
			String sTemp(s._str);
			swap(_str, sTemp._str);
		}
		return *this;
	}*/


private:
	char* _str;
};

/*
Test2中调用没有初始化列表的拷贝构造函数,最终调用析构函数没有问题
*/
void Test2()
{
	String s1("hello");
	String s2(s1);
}

// Test3中调用没有初始化列表的拷贝构造函数,最终调用析构函数会程序中断
String Test3()
{
	String s1("sad");
	return s1;
}

void Test4()
{
	String s1("sad");
	String s2("happy");
	s1 = s2;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值