CD39.【C++ Dev】string类的模拟实现(3)

目录

1.resize

代码

测试代码

运行结果

2.operator<<

测试代码

运行结果

其他说明

 3.operator<<

版本1

继续改进

版本2

优化:版本3

解决方法2:使用缓冲数组

4.字符串比较(relational operators)

分类讨论

代码实现

看VS的STL库实现

运行结果验证

反汇编验证

自主实现

operator<

operator>=

operator==

operator!=

operator>

operator<=


承接CD38.【C++ Dev】string类的模拟实现(2)文章

1.resize

之前在CD36.【C++ Dev】STL库的string的使用 (下)文章提到过resize和reserve的区别,

reserve只是扩容,而resize是扩容+填值初始化(默认填\0) resize会让size和capacity都变(size变的原因是填值初始化)

 依照STL标准实现,

可以复用CD37.【C++ Dev】string类的模拟实现(1)文章的reserve函数,代码如下:

参数n可以小于等于或大于或string的大小_size,需要分类讨论:

1.n<=_size

将原本字符串的前n个字符用\0覆盖

2.n>_size

扩容后的字符串的后(n-_size)个字符用\0覆盖

情况1和2都需要修改_size

代码

void resize(size_t n, char c='\0')
{
	if (n <= _size)
	{
		_size = n;//前n个字符不要动,需要保留
		_str[_size] = '\0';
	}
	else
	{
		reserve(n+1);//+1是放\0
		memset(_str + _size, c, n+1 - _size);
        _str[n]='\0';//如果c不是\0,需要自行用\0中止
		_size = n;
	}
}

测试代码

void test15()
{
	mystl::string str1("teststring");
	mystl::string str2("teststring");
	str1.resize(4);
	str2.resize(15);
	return;
}

下断点到return;

运行结果

void test15()
{
	mystl::string str1("teststring");
	str1.resize(15,'x');
	return;
}

2.operator<<

依照STL标准实现,

放在mystl的命名空间里面

std::ostream& operator<< (std::ostream& os, const string& str)
{
	for (char it : str)
		os << it;
	return os;
}

或者放在mystl的命名空间外面,但string需要改成mystl::string

测试代码

void test16()
{
	mystl::string str("teststring");
	std::cout << str << std::endl;
	return;
}

运行结果

其他说明

operator<<必须引用返回,因为ostream是不能拷贝的,cplusplus网有说明,

https://legacy.cplusplus.com/reference/ostream/ostream/ostream/

 3.operator<<

 依照STL标准实现,

这样写的错误的:

std::istream& operator>> (std::istream& is, string& str)
{
	is >> str._str;
	return is;
}

运行以下测试代码:

void test17()
{
	mystl::string str;
	std::cin >> str;
	std::cout << str;
}

void test17()
{
	mystl::string str;
	str.~string();
	std::cin >> str;
	std::cout << str;
}

 错误的几个原因:

1.虽然_str可以访问指向的内存空间,也有可能出现空间不够的情况,程序可能会越界访问,需要有动态扩容机制

自己实现的构造函数最多可以容纳10个字符

3.没有遵守cin的规则:遇到空格或\0停止读取

解决方法:使用operator+=,会间接调用reserve函数

改成这样仍然有问题:

std::istream& operator>> (std::istream& is, string& str)
{
	char ch;
	is >> ch;
	while (ch != ' ' && ch != '\n')
	{
		is >> ch;
		str += ch;
	}
	return is;
}

 while循环并没有退出,仍然在等待输入:

原因:和cin自身的特点有关系,空格和换行是用于分割的,默认不读空格和换行

解决方法:要将每个字符都读取到,就需要调用get函数

https://legacy.cplusplus.com/reference/istream/istream/get/

版本1

std::istream& operator>> (std::istream& is, string& str)
{
	char ch = is.get();
	while (ch != ' ' && ch != '\n')
	{
		str += ch;
		ch = is.get();
	}
	return is;
}

或者将is.get()替换为getchar(),效果等价

运行结果:

继续改进

上面的代码仍然有问题,cin在读取string类字符串之前,会将字符串清空

对比STL库:

void test18()
{
	std::string str;
	std::cin >> str;
	std::cout << str;
	std::cout << std::endl << "------------" << std::endl;
	std::cin >> str;
	std::cout << str;
	return;
}

所以应该先调用clear():

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

版本2

std::istream& operator>> (std::istream& is, string& str)
{
	str.clear();
	char ch = is.get();
	while (ch != ' ' && ch != '\n')
	{
		str += ch;
		ch = is.get();
	}
	return is;
}

测试代码:

void test18()
{
	mystl::string str;
	std::cin >> str;
	std::cout << str;
	std::cout << std::endl << "------------" << std::endl;
	std::cin >> str;
	std::cout << str;
	return;
}

运行结果:

还要清除前导空格和前导换行

char ch= is.get();
while (ch == ' ' || ch == '\n')
	ch = is.get();//清除前导空格和前导换行

while (ch != ' ' && ch != '\n')
{
    //......
}

运行结果:

 

优化:版本3

当字符串较长时,+=会造成多次扩容,消耗时间,验证:

每扩容一次就打印一次,修改reserve函数:

运行结果:

解决方法1:提前开辟较大的空间

这样写不太好,如果字符串太短,会造成空间的浪费

解决方法2:使用缓冲数组

定义一个具有固定大小的缓冲数组buffer[256],注意:还要处理剩下的字符,即当buffer还有内容时(i>0)

std::istream& operator>> (std::istream& is, string& str)
{
	str.clear();
	char buffer[256] = { '\0' };
	int i = 0;
	char ch= is.get();
	while (ch == ' ' || ch == '\n')
		ch = is.get();//清除前导空格和前导换行

	while (ch != ' ' && ch != '\n')
	{
		buffer[i++] = ch;
		if (i == 255)//最后一个位子给\0
		{
			buffer[i] = '\0';
			str += buffer;
			i = 0;
		}
		ch = is.get();
	}
	if (i > 0)
	{
		buffer[i] = '\0';
		//如果buffer还有剩余,执行此循环
		for (int k = 0; k <= i; k++)
		{
			str += buffer[k];
		}
	}
	return is;
}

运行结果:

4.字符串比较(relational operators)

分类讨论

字符串长度相等,

字符串长度不相等,不能直接调用strcmp

代码实现

无论字符串长度是否相等,都不能调用strcmp,strcmp比较结束的标志是\0,是否结束看的是string的大小size

看VS的STL库实现

void test20()
{
	 std::string str1;
	 std::string str2;
	 str1.push_back('\0');
	 str1.push_back('1');
	 str1.push_back('2');
	 str2.push_back('\0');
	 str2.push_back('1');
	 str2.push_back('3');
	 std::cout << (str1 < str2) << std::endl;
	return;
}
运行结果验证

:很明显越过\0继续向后比较

反汇编验证

下断点到str1 < str2;

转到反汇编后继续下断点:

F11进入call调用的例程:

再下断点到call调用的比较函数compare

 F11进入call调用的例程:

发现微软给出的注释:说明是否结束看的是string的大小size

// compare [0, size()) with _Right

可以使用memcmp,用参数num来接收size

自主实现

operator<

若字符串长度相等

bool operator< (const string& s)
{
	if (_size == s._size)
		return memcmp(_str, s._str, _size-1);//\\末尾的'\0'不用比
	else//lhs.size() == rhs.size()
	{
        //......
	}
}

若字符串长度不相等,分两种情况:

1.结果在\0之前就能比较出来

2.结果在\0之后才能比较出来

那就要设两个指针分别遍历:

先比公共部分:

size_t l = 0;
size_t r = 0;
while (l < _size && r < s._size)
{
	if (_str[l] < s._str[r])
		return 1;
	else if (_str[l] > s._str[r])
		return 0;
	else//_str[l] == s._str[r]则继续比较
	{
		l++;
		r++;
	}
}

循环退出的两种情况:l == _size或者r == s._size,之后分别讨论:

if (l < _size&& r == s._size)
{
    return 0;
}
if  (r < s._size&&l == _size)
{
    return 1;
}
return 0;//两字符串相等,可以不用memcmp

因此有:

bool operator< (const string& s) const
{
		size_t l = 0;
		size_t r = 0;
		while (l < _size && r < s._size)
		{
			if (_str[l] < s._str[r])
				return 1;
			else if (_str[l] > s._str[r])
				return 0;
			else//_str[l] == s._str[r]则继续比较
			{
				l++;
				r++;
			}
		}
		if (l < _size&& r == s._size)
		{
			return 0;
		}
		if  (r < s._size&&l == _size)
		{
			return 1;
		}
		return 0;//两字符串相等
}

其实有更简单的写法:

bool operator< (const string& s) const
{
	int ret = memcmp(_str, s._str, std::min(_size, s._size));
	return ret == 0 ? _size < s._size : ret < 0;
}

memcmp的返回值只有三种:-1,0,1

operator>=

复用operator<

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

	//_size==s._size
	size_t i = 0;
	while(i < _size)
	{
		if (_str[i] != s._str[i])
			return 0;
		i++;
	}
	return 1;
}

还有更简单的写法:

bool operator== (const string& s) const
{
    //_size==s._size写在memcmp前面,提高运行速度
	return _size == s._size && memcmp(_str,s._str,_size)==0
}
operator!=

复用operator==

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

复用operator>=和operator!=

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

复用operator>

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

或者复用operator<和operator==

测试题目:

https://www.nowcoder.com/practice/963e455fdf7c4a4a997160abedc1951b

代码:

#include <cstring>
#include <assert.h>
#include <iostream>
#include <algorithm>
using namespace std;
namespace mystl
{
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;
		string(const char* str = "")//""表示一个\0
			:_size(strlen(str))
			, _capacity(_size + 10)
			, _str(new char[_capacity + 1])
		{
			strcpy(_str, str);
		}

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

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

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

		size_t size() const
		{
			return _size;
		}

		iterator begin()
		{
			return _str;
		}

		const_iterator begin() const
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

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

		void push_back(char c)
		{
			if (_size == _capacity)
			{
				reserve(_capacity * 2);
			}
			_str[_size++] = c;
			_str[_size] = '\0';
		}

		void reserve(size_t n = 0)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];//+1是为\0准备的
				memmove(tmp, _str, _size + 1);//+1是为\0准备的
				delete[] _str;
				_str = tmp;
				_capacity = n;
				//std::cout << "reserve扩容为:" << _capacity << std::endl;
			}
		}

		string& append(const char* str)
		{
			size_t len = strlen(str);
			if (len + _size > _capacity)
				reserve(len + _size);
			memmove(_str + _size, str, len + 1);
			_size += len;
			return *this;
		}

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

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

		string& insert(size_t pos, size_t n, char c)
		{
			assert(pos <= _size);
			if (pos + n > _capacity)//注意不是和_size比较
				reserve(pos + n);//至少扩容到pos + n
			size_t i = _size;//_str[i]为\0
			while (i >= pos && i != npos)
			{
				_str[i + n] = _str[i];
				i--;
			}

			memset(_str + pos, c, n);
			_size += n;
			return *this;
		}

		string& insert(size_t pos, const char* s)
		{
			size_t n = strlen(s);
			assert(pos <= _size);
			if (pos + n > _size)
			{
				reserve(pos + n);
			}
			size_t i = _size;//_str[i]为\0
			while (i >= pos && i != npos)
			{
				_str[i + n] = _str[i];
				i--;
			}

			memmove(_str + pos, s, n);
			_size += n;
			return *this;
		}

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

		string& erase(size_t pos = 0, size_t len = npos)
		{
			assert(pos <= _size);
			if (len == npos || pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				size_t i = pos + len;
				while (i < _size)
					_str[pos++] = _str[i++];
				_str[pos + len] = '\0';
				_size -= len;
			}
			return *this;
		}

		size_t find(const string& str, size_t pos = 0) const
		{
			assert(pos < _size);
			const char* ret_ptr = strstr(_str, str.c_str());
			if (ret_ptr == nullptr)
				return npos;
			return ret_ptr - _str;
		}

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

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

		string substr(size_t pos = 0, size_t len = npos) const
		{
			assert(pos < _size);
			string ret;
			ret._size = len;
			if (len == npos || pos + len >= _size)//从pos处截取到末尾
				ret._size = _size - pos;

			ret.reserve(ret._size);
			memmove(ret._str, _str + pos, ret._size);

			if (ret._str[ret._size] != '\0')
				ret._str[ret._size] = '\0';
			return ret;
		}

		void resize(size_t n, char c = '\0')
		{
			if (n <= _size)
			{
				_size = n;
				_str[_size] = '\0';
			}
			else
			{
				reserve(n + 1);
				memset(_str + _size, c, n + 1 - _size);
				_str[n] = '\0';
				_size = n;
			}
		}

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

		bool operator< (const string& s) const
		{
			size_t l = 0;
			size_t r = 0;
			while (l < _size && r < s._size)
			{
				if (_str[l] < s._str[r])
					return 1;
				else if (_str[l] > s._str[r])
					return 0;
				else//_str[l] == s._str[r]则继续比较
				{
					l++;
					r++;
				}
			}
			if (l < _size && r == s._size)
			{
				return 0;
			}
			if (r < s._size && l == _size)
			{
				return 1;
			}
			return 0;//两字符串相等
		}

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

		bool operator==(const string& s) const
        {
		    if (_size != s._size)
			    return 0;

		    //_size==s._size
		    size_t i = 0;
		    while(i < _size)
		    {
		    	if (_str[i] != s._str[i])
		    		return 0;
		    	i++;
		    }
		    return 1;
	    }

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

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

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

		friend std::istream& operator>> (std::istream& is, string& str);
	private:
		size_t _size;
		size_t _capacity;
		char* _str;
		const static size_t npos;
	};
	const size_t string::npos = -1;
	std::ostream& operator<< (std::ostream& os, const string& str)
	{
		for (char it : str)
			os << it;
		return os;
	}

	std::istream& operator>> (std::istream& is, string& str)
	{
		str.clear();
		char buffer[256] = { '\0' };
		int i = 0;
		char ch = is.get();
		while (ch == ' ' || ch == '\n')
			ch = is.get();//清除前导空格和前导换行

		while (ch != ' ' && ch != '\n')
		{
			buffer[i++] = ch;
			if (i == 255)//最后一个位子给\0
			{
				buffer[i] = '\0';
				str += buffer;
				i = 0;
			}
			ch = is.get();
		}
		if (i > 0)
		{
			buffer[i] = '\0';
			//如果buffer还有剩余,执行此循环
			for (int k = 0; k <= i; k++)
			{
				str += buffer[k];
			}
		}
		return is;
	}
}
int mystrcmp(const char* src, const char* dst);

int main() {

    char s1[100] = { 0 };
    char s2[100] = { 0 };

    cin.getline(s1, sizeof(s1));
    cin.getline(s2, sizeof(s2));

    int ret = mystrcmp(s1, s2);

    cout << ret << endl;

    return 0;
}

int mystrcmp(const char* src,const  char* dst) 
{
     mystl::string str1=src;
     mystl::string str2=dst;
	if (str1 > str2)
		return 1;
	else if (str1 == str2)
		return 0;
	else //str1 < str2
		return -1;
}

提交结果:

### NS-3 安装教程 #### 1. 系统需求 NS-3 的安装需要满足一定的系统环境要求。它主要依赖于 C++ 编译器(如 g++ 或 clang++),Python 版本需达到 3.36 及以上,还需要具备 CMake 和构建工具(如 make、ninja 或 Xcode)[^2]。 #### 2. 下载 NS-3 安装包 可以通过 Linux Shell 获取 `ns-allinone-3.32` 套件。该套件包含了 NS-3 所需的核心文件及其依赖项[^1]。 ```bash wget https://www.nsnam.org/releases/ns-allinone-3.32.tar.bz2 tar jxf ns-allinone-3.32.tar.bz2 cd ns-allinone-3.32/ ``` #### 3. 安装依赖库 为了成功运行 NS-3,必须先安装其所需的依赖软件包。以下是 Ubuntu 系统下的典型命令: ```bash sudo apt update sudo apt install build-essential autoconf automake libxmu-dev python3 python3-tk \ gcc g++ gdb valgrind git-core cvs mercurial bzr qt5-qmake qtbase5-dev \ wireshark tcpdump sqlite3 libsqlite3-dev doxygen graphviz uncrustify python3-pip \ cmake libc6 libc6-dbg libc6-dev pkg-config zlib1g zlib1g-dbg zlib1g-dev \ screen gnuplot-x11 mpich libmpich-dev openmpi-bin libopenmpi-dev autoconf-archive \ texlive-fonts-extra texlive-latex-extra dvipng psmisc ``` 上述命令涵盖了大部分必要的开发工具和库支持。 #### 4. 配置过程 进入解压后的目录并执行配置脚本。如果一切正常,终端会显示如下消息:“&#39;configure&#39; finished successfully (1m23.455s)”[^3]。 ```bash ./build.py --enable-examples --enable-tests ``` #### 5. 编译 NS-3 完成配置后即可启动编译流程。这一步可能会花费较长时间,具体取决于计算机性能。 ```bash ./waf configure ./waf build ``` #### 6. 测试验证 为确保安装无误,可以运行测试程序以确认各模块的功能是否正常。 ```bash ./test.py ``` #### 7. 使用拓扑生成器简化网络设计 对于复杂的仿真场景,建议利用 NS-3 提供的 **拓扑生成器** 工具。它可以显著减少手动配置的工作量,例如创建网络设备、分配 MAC 地址、绑定节点与信道等操作均能被自动化处理[^4]。 --- ### 示例代码:简单的点对点通信模拟 以下是一个基本的例子,展示如何建立两个节点之间的点对点链路。 ```python import ns.core import ns.network import ns.point_to_point import ns.applications def main(): nodes = ns.network.NodeContainer() nodes.Create(2) pointToPoint = ns.point_to_point.PointToPointHelper() pointToPoint.SetDeviceAttribute("DataRate", ns.core.StringValue("5Mbps")) pointToPoint.SetChannelAttribute("Delay", ns.core.StringValue("2ms")) devices = pointToPoint.Install(nodes) stack = ns.internet.InternetStackHelper() stack.Install(nodes) address = ns.internet.Ipv4AddressHelper() address.SetBase("10.1.1.0", "255.255.255.0") interfaces = address.Assign(devices) if __name__ == "__main__": main() ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhangcoder

赠人玫瑰手有余香,感谢支持~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值