关于string类

string类详解与模拟实现

一.标准库中的string 类

1.1 关于string 类的了解

在使用string类的时候,必须包含#include头文件以及using namespace std:

1.2关于auto和范围for

1.auto 现在的用法是suto 不在是一个存储类ing指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期的推到完成。

2.在auto 做声明类型指针时,用auto 和auto* 没有任何区别,但是用auto 声明时必须加上&

3.当在同一行声明多个定义的时候,这些类型必须时相同的类型,否则编译器会报错,因为编译器只会对第一个类型进行推导,然后用推导出来的类型来定义其他的变量。

4.它不能作为函数的参数,可以做返回值,但是建议做返回值时谨慎使用。

5.auto 不能直接用来声明数组

#include<iostream>
using namespace std;

int func1()
{
	return 10;
}

//不能做参数
//void  func2(auto a)
//{
//
//	return 0;
//}

auto func3()
{
	return 4;
}


int main()
{
	int a = 10;
	auto b = a;
	auto c = 'a';
	auto d = func1();

	//auto e;//这里会报无法修改左值的错误
	//因为 auto 本身不代表具体类型,它需要根据初始化表达式来确定变量的实际类型。
	// 如果没有初始化值(如 auto e;),编译器无法推断 e 应该是什么类型,因此会报错。
	cout << typeid(a).name() << endl;
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	cout << typeid(d).name() << endl;

	int x = 10;
	auto y = &x;
	auto* z = &x;
	auto& m = x;

	cout << typeid(x).name() << endl;
	cout << typeid(y).name() << endl;
	cout << typeid(z).name() << endl;
	cout << typeid(m).name() << endl;

	auto aa = 1, bb = 2;
	//auto cc = 3, dd = 5.0;//auto 必须始终推导为同一类型

	//数组不能具有其中包含“auto”的元素类型
	//auto arry[] = { 1,2,3,4,5 };
	return 0;
}

关于auto 的争取用法;


//auto用法
int main()
{
	std::map<std::string, std::string> dict = { { "apple", "苹果" },{ "orange",
"橙子" }, {"pear","梨"} };
	//在这里auto 可以在std::map<std::string, std::string>::iterator it = dict.begin();
	//把std::map<std::string, std::string>::iterator 这个替换成auto
	auto it = dict.begin();
	while (it != dict.end())
	{
		cout << it->first << ":" << it->second << endl;
		++it;
	}
	return 0;
}

关于范围for

1. 对于一个有范围的集合而言,C++中引入例如基于范围的for循环,for循环后的括号由冒号" : "分为两个部分:第一部分时范围内用于迭代的变量,第二部分表示被迭代的范围,自动进行迭代,自动取数据,自动判断结束。

2.范围for可以作用到数组和容器对象上进行遍历。

3.范围for的底层很简单,就是迭代器

{
	int arry[] = { 1,2,3,4,5 };
	//在C++98 的遍历
	for (int i = 0; i < sizeof(arry) / sizeof(arry[0]); ++i)
	{
		arry[i] *= 3;
	}
	for (int i = 0; i < sizeof(arry) / sizeof(arry[0]); ++i)
	{
		cout << arry[i] <<endl;
	}

	//C++11的遍历
	for (auto e:arry)
	{
		e *= 3;
	}
	for (auto e : arry)
	{
		cout << e << " " << endl;
	}
	string str("hello word");
	for (auto ch : str)
	{
		cout << ch << endl;
	} 
		cout<<endl;
	return 0;
}

二.string类的模拟实现

2.1经典的string类问题

上面已经对string类进行了简单的介绍,大家只要能够正常使用即可。在面试中,面试官总喜欢让
学生自己来模拟实现string类,最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析
构函数。大家看下以下string类的实现是否有问题?
// 为了和标准库区分,此处使用String
class String
{
public:
/*String()
:_str(new char[1])
{*_str = '\0';}
*/
//String(const char* str = "\0") 错误示范
//String(const char* str = nullptr) 错误示范
String(const char* str = "")
{
// 构造String类对象时,如果传递nullptr指针,可以认为程序非
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);
}

String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认
的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1s2共用同一块内
存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。

2.2关于浅拷贝

浅拷贝,也被叫做位拷贝,编译器只是将对象中的值拷贝过来,如果对象中管理资源,最后就会呆滞多个对象共享投一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一个对象不知道该资源已经被释放,当对此再进行操作时,就会发生访问违法。

2.3关于深拷贝

如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给
出。一般情况都是按照深拷贝方式提供。

2.3.1传统版写法的String

#include<iostream>
#include<assert.h>
using namespace std;

class String
{
public:
	String(const char* str = " ")
	{
		//构造String类对象,如果传递nullpter指针,可以认为程序非法
		if (nullptr == str)
		{
			assert(false);
			return;
		}
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	String(const String& s)
		:str(new char[strlen(s.str) + 1])
	{
		strcpy(_str, s.s_tr);
	}
	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 = nullputer;
		}
	}
private:
	char* _str;
}

2.3.2现代版写法的String

//现代版写法的String类

class String
{
public:
	String(const char* str = " ")
	{
		if (nullptr == str)
		{
			assert(false);
			return;
		}
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	String(const String& s)
		: _str(nullptr)
		{
		String strTmp(s._str);
		swap(_str, strTmp._str);
		}

	// 对比下和上面的赋值那个实现比较好?
	String& operator=(String s)
	{
		swap(_str, s._str);
		return *this;
	}
		/*
String& operator=(const String& s)
{
if(this != &s)
{
String strTmp(s);
swap(_str, strTmp._str);
}
return *this;
}
*/
	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}
private:
	char* _str;
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值