当前位置: 首页 > article >正文

16.C++STL 3(string类的模拟,深浅拷贝问题)

⭐本篇重点:string类的模拟,自己实现一个简单的string类

⭐本篇代码:c++学习/05.string类的学习 · 橘子真甜/c++-learning-of-yzc - 码云 - 开源中国 (gitee.com)

目录

一. 经典string类的模拟

1.1 深浅拷贝问题

 1.2 使用深拷贝完成经典string类的模拟

a size函数

b 拷贝构造函数

 c 赋值运算符重载

d operator[]重载

二. 现代写法的string类模拟

2.1 拷贝构造函数

 2.2 赋值运算符重载

三. 下篇文章:STL中vector的使用 


一. 经典string类的模拟

        实现一个简单的string类,主要是实现string类的构造函数,析构函数,拷贝构造函数,赋值运算符重载。这个过程需要我们首先理解深浅拷贝的问题。

1.1 深浅拷贝问题

        浅拷贝问题:如果我们初始化一个对象的时候,只是简单的将另一个对象的值赋值给这个对象。比如我们在堆上申请的空间。当我们销毁这两个对象的时候,由于它们指向同一个空间,这个空间就会被销毁两次。程序就会崩溃

测试代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
using namespace std;

namespace yzc
{
	class string
	{
	public:
		string(const char* str = "")
			:_str(new char[strlen(str) + 1])
		{
			cout << "调用构造函数" << endl;
			strcpy(_str, str);
		}

		//拷贝构造
		string(const string& str)
		{
			*this = str;
		}

		~string()
		{
			cout << "调用析构函数" << endl;
			delete[] _str;
		}


		void print()
		{
			cout << _str << endl;
		}
	private:
		char* _str;
	};
}


int main()
{
	yzc::string s1 = "123456";
	yzc::string s2 = "abcdef";
	s1.print();
	s2.print();
	return 0;
}

没有调用拷贝构造函数,可以正常输出。

运行结果如下:

如果我们使用拷贝构造函数

int main()
{
	yzc::string s1 = "123456";
	yzc::string s2(s1);
	return 0;
}

分析代码可知,由于浅拷贝问题。输出一次调用构造函数和两次析构函数后程序崩溃

只有使用深拷贝,我们在拷贝构造函数里面重新申请一份空间,然后重新全部复制才行。 

 1.2 使用深拷贝完成经典string类的模拟

a size函数

        为了完成深拷贝,我们需要定义一个size函数用于求_str的长度。加上const,因为函数内部并不会更改任何值

		int size()const
		{
			return strlen(_str);
		}

b 拷贝构造函数

与构造函数类似,我们重新开辟一份空间,然后使用strcpy进行拷贝

​
		//拷贝构造,使用深拷贝完成拷贝
		string(const string& s)
			:_str(new char[s.size()])
		{
			strcpy(_str, s._str);
		}

测试代码: 

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
using namespace std;

namespace yzc
{
	class string
	{
	public:
		string(const char* str = "")
			:_str(new char[strlen(str) + 1])	//由于C语言字符串后面都带'\0',需要加上1
		{
			strcpy(_str, str);
		}

		拷贝构造
		//string(const string& str)
		//{
		//	*this = str;
		//}

		//拷贝构造,使用深拷贝完成拷贝
		string(const string& s)
			:_str(new char[s.size() + 1])    //一定要注意要加1,否则会越界访问
		{
			strcpy(_str, s._str);
		}

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

		int size()const
		{
			return strlen(_str);
		}

		void print()
		{
			cout << _str << endl;
		}
	private:
		char* _str;
	};
}


int main()
{
	yzc::string s1 = "123456";
	yzc::string s2(s1);
	s1.print();
	s2.print();
	return 0;
}

测试结果 

 c 赋值运算符重载

与拷贝构造函数一样,要注意深浅拷贝的问题

		//赋值运算符重载
		string& operator=(const string& s)
		{
			if (this != &s)//地址不同才进行赋值
			{
				delete[] _str; //释放原地址空间,防止内存泄漏
				//定义中间变量tmp用于拷贝
				char* tmp = new char[s.size() + 1]; //注意要 + 1
				strcpy(tmp, s._str);
				_str = tmp;
				
			}
			return *this; //返回当前对象,为了支持 a = b = c
		}

 测试:主函数代码如下

int main()
{
	yzc::string s1 = "123456";
	yzc::string s2 = "abcdef";
	
	yzc::string s3 = s1;
	yzc::string s4;
	s4 = s2;

	s3.print();
	s4.print();
	return 0;
}

测试结果:

d operator[]重载

我们还能重载[]这个操作符,方便我们遍历整个字符串

直接输入i,返回_str[i]即可(注意const) 

		char& operator[](size_t i)
		{
			assert(i < size());
			return _str[i];
		}

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

测试代码:

int main()
{
	yzc::string s1 = "159753468244";
	for (int i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << " ";
	}
	cout << endl;
	return 0;
}

运行结果:

二. 现代写法的string类模拟

2.1 拷贝构造函数

         为了提高代码的复用,我们在拷贝构造函数中使用构造函数去构造一个对象,然后交换当前对象和这个对象。

代码如下:

		//现代版写法
		string(const string& s)
			:_str(nullptr)
		{
			string strTmp(s._str); //使用构造函数将s._str构造一个tmp
			swap(_str, strTmp._str);//交换_str和tmp
		}

交换后,strTmp由于是局部变量就直接被销毁了! 而我们创建的对象被保留

 2.2 赋值运算符重载

我们使用传值方法传参,交换_str后我们成功创建的对象。且不会影响传入的对象


		//现代版写法
		string& operator=(string s) //不使用引用传参,而是传值
		{
			swap(_str, s._str);
			return *this;
		}

测试代码

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

namespace yzc
{
	class string
	{
	public:
		string(const char* str = "")
			:_str(new char[strlen(str) + 1])	//由于C语言字符串后面都带'\0',需要加上1
		{
			strcpy(_str, str);
		}

		拷贝构造,使用深拷贝完成拷贝
		//string(const string& s)
		//	:_str(new char[s.size() + 1]) //注意要加1
		//{
		//	strcpy(_str, s._str);
		//}

		//现代版写法
		string(const string& s)
			:_str(nullptr)
		{
			string strTmp(s._str); //使用构造函数将s._str构造一个tmp
			swap(_str, strTmp._str);//交换_str和tmp
		}

		赋值运算符重载
		//string& operator=(const string& s)
		//{
		//	if (this != &s)//地址不同才进行赋值
		//	{
		//		delete[] _str; //释放原地址空间,防止内存泄漏
		//		//定义中间变量tmp用于拷贝
		//		char* tmp = new char[s.size() + 1];
		//		strcpy(tmp, s._str);
		//		_str = tmp;
		//		
		//	}
		//	return *this; //返回当前对象,为了支持 a = b = c
		//}

		//现代版写法
		string& operator=(string s) //不使用引用传参,而是传值
		{
			swap(_str, s._str);
			return *this;
		}

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


		size_t size()const
		{
			return strlen(_str);
		}

		void print()
		{
			cout << _str << endl;
		}
	private:
		char* _str;
	};
}


int main()
{
	yzc::string s1 = "123456";
	yzc::string s2(s1);
	yzc::string s3 = s1;
	s1.print();
	s2.print();
	s3.print();
	return 0;
}

运行结果:

三. 下篇文章:STL中vector的使用 


http://www.kler.cn/a/408338.html

相关文章:

  • leetcode day13 贪心 45+55
  • C++初阶(十五)--STL--list 的深度解析与全面应用
  • NoteExpress导入知网论文无法智能更新题录的处理方法
  • Java算法OJ(10)哈希表练习
  • 计算机网络期末试题及答案(整理)
  • Typora+PicGo+云服务器搭建博客图床
  • 〔 MySQL 〕中三种重要的日志类型
  • Java网络编程 - cookiesession
  • Vulnhub靶场 Jangow: 1.0.1 练习
  • C语言超详细教程
  • 挂壁式空气净化器哪个品牌的质量好?排名top3优秀产品测评分析
  • 网络性能及IO性能测试工具
  • golang实现TCP服务器与客户端的断线自动重连功能
  • 优先算法 —— 双指针系列 - 复写零
  • 青训营刷题笔记17
  • [自动化]获取每次翻页后的页面 URL
  • Java核心特性解析:方法、Stream流、文件与IO详解
  • 每日OJ_牛客_合唱队形_DP_C++_Java
  • 数据库连接池(二)
  • Vue v-if 与 v-for 使用指南:优先级、注意事项及常见错误防范
  • Independent Component Analysis
  • 如何利用ros搭建虚拟场景通过仿真机器人完成一次简单的SLAM建图、导航规划(超简单)?——学习来源:机器人工匠阿杰
  • SpringBoot多文件上传
  • springboot3如何集成knife4j 4.x版本及如何进行API注解
  • Spring集成测试
  • 电子应用设计方案-21:智能取暖系统方案设计