【C++】学习笔记——string_5

news2025/2/25 7:19:21

文章目录

  • 六、string类
    • 7. string类的模拟实现
    • 8. string类的模拟实现的完整代码
      • string.h头文件
      • test.c源文件
    • 9. string收尾
      • 写时拷贝
  • 未完待续


六、string类

7. string类的模拟实现

我们之前讲了实现 insert ,但是那个插入函数仅仅是在 pos 位置插入一个字符而且,我们并没有实现在 pos 位置插入一个字符串。所以我们现在将其补充上。

// 在 pos 位置插入一个字符串
void insert(size_t pos, const char* str)
{
	assert(pos <= _size);
	size_t len = strlen(str);
	// 空间不够需要扩容
	if (_size + len > _capacity)
	{
		reserve(_size + len);
	}
	size_t end = _size + len;
	while (end >= pos + len)
	{
		_str[end] = _str[end - len];
		--end;
	}
	strncpy(_str + pos, str, len);
}

当难以理解的时候,记得画图哦,我们来看看结果:

#include"string.h"
using namespace my;

int main()
{
	string s("hello,world");
	s.insert(4, "AAAAAAA");
	std::cout << s.c_str() << std::endl;
	return 0;
}

在这里插入图片描述
没问题。其实我们发现,有了 insert 之后,前面的 push_back()append 好像可以复用这的代码,我们来调整一下。

void push_back(char ch)
{
	//if (_size == _capacity)
	//{
	//	// 扩容2倍
	//	reserve(_capacity == 0 ? 4 : 2 * _capacity);
	//}

	//_str[_size] = ch;
	//++_size;
	//_str[_size] = '\0';
	insert(_size, ch);
}

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

	 从末尾开始,拷贝新字符串
	//strcpy(_str + _size, str);	
	//_size += len;
	insert(_size, str);
}

接下来我们再把 swap 给实现一下,有人会说哈,swap 在库里面就有,为啥还要我们手动实现呢?我给大家看看库里的 swap 是怎样的。
在这里插入图片描述
我们发现,库里的swap整整调用了3次拷贝和1次析构,虽然能用,但是它非常搓,所以我们要实现一个对我们来说更加好用的 swap

void swap(string& s)
{
	// 调用库里的swap,直接交换成员即可,不需要创建一个新的对象
	std::swap(_str, s._str);
	// 加上std:: 是让其直接去std域找,避免找到当前的成员函数swap
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}
#include"string.h"
using namespace my;

int main()
{
	string s("hello,world");
	string s1("1234567890");
	
	s.swap(s1);
	std::cout << s.c_str() << std::endl << s1.c_str() << std::endl;
	return 0;
}

在这里插入图片描述
既然库里的 swap 对我们来说很挫,但是我们应该怎样才能防止别人使用库里的函数呢?
在这里插入图片描述
库里面有个 swap ,成员函数有个 swap ,这里怎么还有一个非成员函数的 swap
在这里插入图片描述
这里的 swap 原来直接调用的是成员函数 swap ,那么非成员函数的 swap 是全局的,库里的 swap 是全局的,为啥没发生冲突?为啥就会先用非成员函数的 swap ?我在模板那篇提到过,因为 库里的 swap 是模板当有现成的函数时,优先使用现成的。这下就完美解决了使用库里的swap了。

// string类外
void swap(string& x, string& y)
{
	x.swap(y);
}

接下来实现 find 函数。

// 查找一个字符
size_t find(char ch, size_t pos = 0) const
{
	for (size_t i = pos; i < _size; ++i)
	{
		if (_str[i] == ch)
			return i;
	}

	return npos;
}
// 查找一个字串
size_t find(const char* sub, size_t pos = 0) const
{
	assert(pos < _size);
	const char* p = strstr(_str + pos, sub);
	// 找到了
	if (p)
	{
		return p - _str;
	}
	// 没找到
	return npos;
}

接下来实现 substr 函数。
在这里插入图片描述

string substr(size_t pos = 0, size_t len = npos)
{
	string sub;
	if (len >= _size - pos)
	{
		for (size_t i = pos; i < _size; ++i)
		{
			sub += _str[i];
		}
	}
	else
	{
		for (size_t i = pos; i < pos + len; ++i)
		{
			sub += _str[i];
		}
	}
	return sub;
}

验证验证:

#include"string.h"
using namespace my;

int main()
{
	string s("hello,world");
	
	size_t pos = s.find(',');
	std::cout << s.substr(pos, 3).c_str() << std::endl;
	return 0;
}

在这里插入图片描述
再就是重载比较。

// 重载成全局函数
bool operator==(const string& s1, const string& s2)
{
	// strcmp 若是相等则返回0
	int ret = strcmp(s1.c_str(), s2.c_str());
	return ret == 0;
}

bool operator<(const string& s1, const string& s2)
{
	int ret = strcmp(s1.c_str(), s2.c_str());
	return ret < 0;
}

bool operator<=(const string& s1, const string& s2)
{
	return s1 < s2 || s1 == s2;
}

bool operator>(const string& s1, const string& s2)
{
	return !(s1 <= s2);
}

bool operator>=(const string& s1, const string& s2)
{
	return !(s1 < s2);
}

bool operator!=(const string& s1, const string& s2)
{
	return !(s1 == s2);
}

实现流插入和流提取:

using namespace std;

ostream& operator<<(ostream& out, const string& s)
{
	// 没有访问私有成员,不需要友元
	for (auto ch : s)
	{
		out << ch;
	}

	return out;
}

istream& operator>>(istream& in, string& s)
{
	// 需要将其内容清空
	s.clear();
	char ch;
	// cin 读取不到 ' ' 和 '\n'
	ch = in.get();
	// 减少扩容
	char buff[128];
	size_t i = 0;
	while (ch != ' ' && ch != '\n')
	{
		buff[i++] = ch;
		if (i == 127)
		{
			buff[127] = '\0';
			s += buff;
			i = 0;
		}

		ch = in.get();
	}

	if (i > 0)
	{
		buff[i] = '\0';
		s += buff;
	}

	return in;
}

顺便实现 clear

// 成员函数
void clear()
{
	_size = 0;
	_str[_size] = '\0';
}

检验检验:

#include"string.h"
using namespace my;

int main()
{
	// 刚刚展开了std,这里避免冲突
	my::string s;
	cin >> s;
	cout << s;
	return 0;
}

在这里插入图片描述
很好,空格前的都被读取到了。
再来实现 getline 。getline就是读取一行嘛,相信实现了流提取运算符,实现一个 getline 肯定非常轻松。

istream& getline(istream& in, string& s)
{
	// 需要将其内容清空
	s.clear();
	char ch;
	// cin 读取不到 ' ' 和 '\n'
	ch = in.get();
	// 减少扩容
	char buff[128];
	size_t i = 0;
	while (ch != '\n')
	{
		buff[i++] = ch;
		if (i == 127)
		{
			buff[127] = '\0';
			s += buff;
			i = 0;
		}

		ch = in.get();
	}

	if (i > 0)
	{
		buff[i] = '\0';
		s += buff;
	}

	return in;
}

再来检验:

#include"string.h"
using namespace my;

int main()
{
	my::string s;
	getline(cin, s);
	cout << s;
	return 0;
}

在这里插入图片描述

在实现拷贝构造函数时,我们写了一个非常传统的写法,这里再给大家实现一种新式写法:

// 传统写法
string(const string& s)
{
	_str = new char[s._capacity + 1];
	strcpy(_str, s._str);
	_size = s._size;
	_capacity = s._capacity;
}

// 新式写法
string(const string& s)
{
	string tmp(s._str);
	swap(tmp);
}

这里新式写法本质上就是调用构造函数,然后让 this指针 指向新的构造的 sring 类 。同样,赋值重载也能使用新式写法。

// 传统写法
string& operator=(const string& s)
{
	char* tmp = new char[s._capacity + 1];
	strcpy(tmp, s._str);
	
	delete[] _str;
	_str = tmp;
	_size = s._size;
	_capacity = s._capacity;

	return *this;
}

// 新式写法
string& operator=(const string& s)
{
	string tmp(s);
	swap(tmp);

	return *this;
}

这里赋值重载的新式写法还能优化(行数),既然在函数内部要调用拷贝构造,为什么不在传参的时候直接调用拷贝构造呢?

string& operator=(string s)
{
	swap(s);

	return *this;
}

8. string类的模拟实现的完整代码

string.h头文件

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

namespace my
{
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		const_iterator begin() const
		{
			return _str;
		}

		const_iterator end() const
		{
			return _str + _size;
		}

		// 默认是空串而不是空指针
		string(const char* str = "")
			:_size(strlen(str))
		{
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

		//string(const string& s)
		//{
		//	_str = new char[s._capacity + 1];
		//	strcpy(_str, s._str);
		//	_size = s._size;
		//	_capacity = s._capacity;
		//}

		string(const string& s)
		{
			string tmp(s._str);
			swap(tmp);
		}

		//string& operator=(const string& s)
		//{
		//	char* tmp = new char[s._capacity + 1];
		//	strcpy(tmp, s._str);
		//	
		//	delete[] _str;
		//	_str = tmp;
		//	_size = s._size;
		//	_capacity = s._capacity;

		//	return *this;
		//}

		string& operator=(string s)
		{
			swap(s);

			return *this;
		}

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

		// 加 const 使其成为 const 成员函数,使 const 对象也能调用这个函数
		size_t size() const
		{
			return _size;
		}

		// 引用返回,可读可写
		inline char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}

		// 针对 const对象 的可读不可写,加 & 是为了减少拷贝
		inline const char& operator[](size_t pos) const
		{
			assert(pos < _size);
			return _str[pos];
		}

		size_t capacity() const
		{
			return _capacity;
		}

		void reserve(size_t n)
		{
			// 只有要扩容的大小比当前容量大才能扩容
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;

				_capacity = n;
			}
		}

		void push_back(char ch)
		{
			//if (_size == _capacity)
			//{
			//	// 扩容2倍
			//	reserve(_capacity == 0 ? 4 : 2 * _capacity);
			//}

			//_str[_size] = ch;
			//++_size;
			//_str[_size] = '\0';

			insert(_size, ch);
		}

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

			 从末尾开始,拷贝新字符串
			//strcpy(_str + _size, str);	
			//_size += len;

			insert(_size, str);
		}

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

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

		char* c_str() const
		{
			return _str;
		}

		// 在 pos 位置插入一个字符
		void insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				// 扩容2倍
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
			}
			size_t end = _size + 1;
			while (end > pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}
			_str[pos] = ch;
			++_size;
		}

		// 在 pos 位置插入一个字符串
		void insert(size_t pos, const char* str)
		{
			assert(pos <= _size);
			size_t len = strlen(str);
			// 空间不够需要扩容
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}
			size_t end = _size + len;
			while (end >= pos + len)
			{
				_str[end] = _str[end - len];
				--end;
			}
			strncpy(_str + pos, str, len);
		}

		// 从 pos 开始,删除 len 个字符,如果 len 是 npos ,则全删
		void erase(size_t pos, size_t len = npos)
		{
			assert(pos < _size);
			// pos + len >= _size 可能会溢出
			if (len == npos || len >= _size - pos)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			strcpy(_str + pos + len, _str + pos);
			_size -= len;
		}

		void resize(size_t n, char ch = '\0')
		{
			if (n <= _size)
			{
				_str[n] = '\0';
				_size = n;
			}
			else
			{
				reserve(n);
				for (size_t i = _size; i < n; ++i)
				{
					_str[i] = ch;
				}
				_str[n] = '\0';
				_size = n;
			}
		}

		void swap(string& s)
		{
			// 直接交换成员即可
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}

		size_t find(char ch, size_t pos = 0) const
		{
			for (size_t i = pos; i < _size; ++i)
			{
				if (_str[i] == ch)
					return i;
			}

			return npos;
		}

		size_t find(const char* sub, size_t pos = 0) const
		{
			assert(pos < _size);
			const char* p = strstr(_str + pos, sub);
			// 找到了
			if (p)
			{
				return p - _str;
			}
			// 没找到
			return npos;
		}

		string substr(size_t pos = 0, size_t len = npos)
		{
			string sub;
			if (len >= _size - pos)
			{
				for (size_t i = pos; i < _size; ++i)
				{
					sub += _str[i];
				}
			}
			else
			{
				for (size_t i = pos; i < pos + len; ++i)
				{
					sub += _str[i];
				}
			}
			return sub;
		}

		void clear()
		{
			_size = 0;
			_str[_size] = '\0';
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	public:
		static const int npos;
	};

	// 静态成员变量在类外部定义
	const int string::npos = -1;

	// string类外
	void swap(string& x, string& y)
	{
		x.swap(y);
	}

	bool operator==(const string& s1, const string& s2)
	{
		// strcmp 若是相等则返回0
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret == 0;
	}

	bool operator<(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret < 0;
	}

	bool operator<=(const string& s1, const string& s2)
	{
		return s1 < s2 || s1 == s2;
	}

	bool operator>(const string& s1, const string& s2)
	{
		return !(s1 <= s2);
	}

	bool operator>=(const string& s1, const string& s2)
	{
		return !(s1 < s2);
	}

	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}

	ostream& operator<<(ostream& out, const string& s)
	{
		// 没有访问私有成员,不需要友元
		for (auto ch : s)
		{
			out << ch;
		}

		return out;
	}

	istream& operator>>(istream& in, string& s)
	{
		// 需要将其内容清空
		s.clear();
		char ch;
		// cin 读取不到 ' ' 和 '\n'
		ch = in.get();
		// 减少扩容
		char buff[128];
		size_t i = 0;
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 127)
			{
				buff[127] = '\0';
				s += buff;
				i = 0;
			}

			ch = in.get();
		}

		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
		}

		return in;
	}

	istream& getline(istream& in, string& s)
	{
		// 需要将其内容清空
		s.clear();
		char ch;
		// cin 读取不到 ' ' 和 '\n'
		ch = in.get();
		// 减少扩容
		char buff[128];
		size_t i = 0;
		while (ch != '\n')
		{
			buff[i++] = ch;
			if (i == 127)
			{
				buff[127] = '\0';
				s += buff;
				i = 0;
			}

			ch = in.get();
		}

		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
		}

		return in;
	}
}

test.c源文件

#include"string.h"
using namespace my;

int main()
{
	// 
	return 0;
}

9. string收尾

我们来看看下面一段程序:

#include"string.h"
using namespace my;

int main()
{
	std::string s("11111");
	cout << sizeof(s) << endl;
	return 0;
}

库里的 string 是多少空间呢?
在这里插入图片描述
嗯?如果是我们写的 string ,那么应该是 12 啊(32位下,64位是 24),为什么库里的 string 是 28呢?其实是因为编译器做了相关的优化,它在原有的基础上还新增了一个 buff[16] 数组,当数据较小的时候会存到 buff 里,比较大才会存到 _str 在堆上开辟的空间里。因为栈上开辟空间比在堆上开辟空间快。这仅仅是在 vs 编译器下是这样的,其他编译器不一定是这样。
在这里插入图片描述

写时拷贝

我们知道,拷贝构造不能是浅拷贝,假如是浅拷贝,那么会导致两个指针指向同一块空间,一个修改另一个也会发生变化,多次析构的问题。所以只能是深拷贝。但是库里的 string 类真的是这样的吗?库里的 string 类的构造函数其实是浅拷贝,但是它有个引用计数,代表有几个对象指向这块空间,如果有对象要修改(写),则重新深拷贝一个空间给其使用,析构的时候则判断引用计数是否为1,不为1则不析构。这样做其实就是赌有的对象不去修改内容,只要有这样的对象,那么就算是一种优化。


未完待续

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1661014.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

提取网页元数据的Python库之lassie使用详解

概要 Lassie是一个用于提取网页元数据的Python库,它能够智能地抓取网页的标题、描述、关键图像等内容。Lassie的设计目的是为了简化从各种类型的网页中提取关键信息的过程,适用于需要预览链接内容的应用场景。 安装 安装Lassie非常简单,可以通过Python的包管理器pip进行安…

多目标跟踪入门介绍

多目标跟踪算法 我们也可以称之为 Multi-Target-Tracking &#xff08;MTT&#xff09;。 那么多目标跟踪是什么&#xff1f; 不难看出&#xff0c;跟踪算法同时会为每个目标分配一个特定的 id 。 由此得出了目标跟踪与目标检测的区别&#xff08;似乎都是用方框来框出目标捏…

PLC设备通过智能网关采用HTTP协议JSON文件对接MES等系统平台

智能网关IGT-DSER集成了多种PLC的原厂协议&#xff0c;方便实现各种PLC、智能仪表通过HTTP协议与MES等各种系统平台通讯对接。PLC内不用编写程序&#xff0c;设备不用停机&#xff0c;通过网关的参数配置软件(下载地址)配置JSON文件的字段与PLC寄存器地址等参数即可。 智能网关…

小程序如何确定会员身份并批量设置会员积分或余额

因为一些原因&#xff0c;商家需要从其它系统里面批量导入会员&#xff0c;确定会员身份&#xff0c;然后给他们设置对应的账户余额。下面&#xff0c;就具体介绍如何进行这种操作。 一、客户进入小程序并绑定手机号 进入小程序&#xff1a;客户打开小程序&#xff0c;系统会自…

利用AI提高内容生产效率的五个方案

目录 如何利用AI提高内容生产效率? ​编辑方向一&#xff1a;自动化内容生成 方向二&#xff1a;内容分发与推广 方向三&#xff1a;内容分析与优化 方向四&#xff1a;图像和音频处理 方向五&#xff1a;自动编辑和校对 如何利用AI提高内容生产效率? 简介&#xff1a…

system函数和popen函数

system函数 #include <stdlib.h> *int system(const char command); system函数在linux中的源代码&#xff1a; int system(const char * cmdstring) {pid_t pid;int status;if(cmdstring NULL){return (1);}if((pid fork())<0){status -1;}else if(pid 0){ //子…

【YashanDB知识库】ycm托管数据库时报错OM host ip:127.0.0.1 is not support join to YCM

问题现象 问题的风险及影响 导致数据库无法托管监控 问题影响的版本 问题发生原因 安装数据库时修改了OM的监听ip为127.0.0.1 解决方法及规避方式 后台修改OM的ip为本机的ip或者0.0.0.0 问题分析和处理过程 1、修改env文件中的om IP地址&#xff0c;修改为0.0.0.0或本机…

G2 - 人脸图像生成(DCGAN)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目录 理论知识DCGAN原理 模型结构逻辑结构物理结构 模型实现前期准备1. 导入第三方库2. 修改随机种子(相同的随机种子&#xff0c;第i次随机的结果是固定的)3.…

Transformers中加载预训练模型的过程剖析

使用HuggingFace的Transformers库加载预训练模型来处理下游深度学习任务很是方便,然而加载预训练模型的方法多种多样且过程比较隐蔽,这在一定程度上会给人带来困惑。因此,本篇文章主要讲一下使用不同方法加载本地预训练模型的区别、加载预训练模型及其配置的过程,藉此做个记…

Java入门——类和对象(上)

经读者反映与笔者考虑&#xff0c;近期以及往后内容更新将主要以java为主&#xff0c;望读者周知、见谅。 类与对象是什么&#xff1f; C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;通过函数调用逐步解决问题。 JAVA是基于面向对…

回溯算法—组合问题

文章目录 介绍应用问题基本流程算法模版例题&#xff08;1&#xff09;组合&#xff08;2&#xff09;电话号码的字母组合 介绍 回溯算法实际上是 一个类似枚举的搜索尝试过程&#xff0c;主要是在搜索尝试过程中寻找问题的解&#xff0c;当发现已不满足求解条件时&#xff0c;…

明火检测实时识别报警:视觉算法助力安全生产管理

背景与现状 在各种工作、生产环境下&#xff0c;明火的存在往往是潜在的安全隐患。无论是加油站、化工园区、仓储场所还是校园&#xff0c;明火一旦失控就会引发火灾&#xff0c;造成严重的人员伤亡和财产损失。传统的明火检查手段主要依赖于人工巡查和定期的消防检查&#xf…

可微分矢量图形光栅化用于编辑和学习

图1. 我们引入了一种通过反向传播将光栅和矢量域联系起来的矢量图形可微分光栅化器。可微分光栅化实现了许多新颖的矢量图形应用。&#xff08;a&#xff09;在几何约束下&#xff0c;通过局部优化图像空间度量&#xff08;如不透明度&#xff09;来实现交互式编辑。&#xff0…

leetcode刷题:对称二叉树

题目&#xff1a; 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true 示例 2&#xff1a; 输入&#xff1a;root [1,2,2,null,3,null,3] 输出&#xff1a;false 提示&#xf…

基于Springboot+Vue的Java项目-毕业就业信息管理系统开发实战(附演示视频+源码+LW)

大家好&#xff01;我是程序员一帆&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &am…

Agent AI:智能代理的未来

&#x1f388;写在前面 &#x1f64b;‍♂️大家好呀&#xff0c;我是超梦梦梦梦 &#x1f64b;‍♂️ 小伙伴们如果在学习过程中有不明白的地方&#xff0c;欢迎评论区留言提问&#xff0c;小梦定知无不言&#xff0c;言无不尽。 目录 一、Agent AI的起源与发展 二、Agent A…

使用GitLab自带的CI/CD功能在K8S集群里部署项目(四)

前置内容&#xff1a; 通过Docker Compose部署GitLab和GitLab Runner&#xff08;一&#xff09; 使用GitLab自带的CI/CD功能在本地部署项目&#xff08;二&#xff09; 使用GitLab自带的CI/CD功能在远程服务器部署项目&#xff08;三&#xff09; 一、K8S集群信息 节点名称…

誉天教育近期开班计划

云计算HCIE 晚班 2024/5/13 大数据直通车 周末班 2024/5/25 数通直通车 晚班 2024/5/27 云服务HCIP 周末班 2024/6/1 云计算HCIP 周未班 2024/6/1 RHCA442 晚班 2024/6/17 周末班&#xff1a;周六-周日9:00-17:00晚 班&#xff1a;周一到周五19:00-21:30注&…

【C++ 内存管理】深拷贝和浅拷贝你了解吗?

文章目录 1.深拷贝2.浅拷贝3.深拷贝和浅拷贝 1.深拷贝 &#x1f34e; 深拷⻉: 是对对象的完全独⽴复制&#xff0c;包括对象内部动态分配的资源。在深拷⻉中&#xff0c;不仅复制对象的值&#xff0c;还会复制对象所指向的堆上的数据。 特点&#xff1a; &#x1f427;① 复制对…

IF:23.2|从实验室到田间,微生物干预提高植物抗逆

期刊&#xff1a;Nature Food 影响因子&#xff1a;23.2 发表时间&#xff1a;2023年10月 本研究介绍了一种名为SynCom的微生物组合&#xff0c;该组合Rhodococcus erythropolis和Pseudomonas aeruginosa两种微生物组成。这两种微生物能够帮助水稻抵抗铝毒害和磷缺乏&…