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

c++_string模拟实现

目录

c_str:

函数定义

1. 函数的返回类型

2. 函数的作用

3. 为什么返回 const char*

构造:string(const char* str = ' ')

 析构:~string()

string 遍历

字符串的大小:size_t size() const

容量大小:size_t capacity() const

char& operator[](size_t pos)是提供对字符串中特定位置字符的访问,并且允许对这个字符进行读写操作。

迭代器iterator _ 范围for

测试函数:   

增删查改

拼接:string& operator+=(char ch)

插入字符 insert

插入字符串 insert

删除字符串

在库中

std::string 中的 erase()

删除单个字符

删除范围内的字符

删除从指定位置开始的多个字符

模拟实现:

缩减字符串的长度:resize

模拟实现

拷贝构造函数(传统写法):string(const string& s)

赋值:string& operator=(const string& s)

交换:swap()

找寻字符和字符串位置:find()

从字符串中提取子字符串:substr

参数说明:

返回值:

判断大小:operator== ,>,>=,<,<=,!= 

流插入流提取:<< ,>>

在进入函数时,添加一个清除函数:clear()。 

提取行getline()

照如图所示的思路:

拷贝构造函数更新(新写法、现代写法):

赋值运算符的现代写法 


主要实现string类的构造、拷贝构造、赋值运算符重载以及析构函数

再开始之前我们需要提供对底层字符数组的直接访问的函数也就是

c_str:

想要对底层字符数组的直接访问。需要提供一个c_str()函数:

以下是对 c_str() 函数的详细解释:

函数定义

cpp复制

const char* c_str()
{
    return _str;
}

1. 函数的返回类型

  • const char*:返回一个指向 const 字符的指针。这意味着返回的指针指向的字符串内容是只读的,不能通过这个指针修改字符串的内容。

2. 函数的作用

  • 返回底层字符数组的指针

    • _str 是一个动态分配的字符数组,存储了字符串的内容,包括终止符 \0

    • c_str() 返回 _str 的指针,允许用户直接访问底层的字符数组。

  • 用途

    • 提供对字符串的直接访问,方便与其他 C 风格的函数或 API 交互。

    • 例如,许多 C 标准库函数(如 printfstrlen 等)需要一个 const char* 类型的参数,c_str() 可以提供这种类型的指针。

3. 为什么返回 const char*

  • 保证只读访问

    • 返回 const char* 指针,确保用户不能通过这个指针修改字符串的内容。这与 std::string 的行为一致,保证了字符串的不可变性。

    • 如果返回 char*,用户可能会修改字符串的内容,这可能会导致不可预测的行为,尤其是在字符串对象的内部逻辑中(如动态内存管理)。

  • 兼容性

    • 返回 const char* 指针,使得 bit::string 类与 C 标准库函数和其他需要只读字符串的 API 兼容。

构造:string(const char* str = ' ')

请阅读并观察下面的代码:

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>


namespace bit
{
	class string
	{
	public:
    //这里是有问题的
	/*	string()
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
		}*/
		//代参的构造函数初始化
		//strlen是运行时去遍历字符串
		// ,遇到'\0'就结束不宜多次调用,这里一共调用了三次
		//string(const char* str)
		//	:_str(new char[strlen(str) + 1])
		//	,_size(strlen(str))
		//	,_capacity(strlen(str))//capacity不包含'\0'
		//{
		//	//要满足能够修改
		//	strcpy(_str, str);
		//}
		//注意初始化列表走的顺序,是按照声明的顺序,所以不能这样
		//string(const char* str)
		//	:_size(strlen(str))
		//	,_str(new char[strlen(str) + 1])
		//	,_capacity(strlen(str))//capacity不包含'\0'
		//{
		//	//要满足能够修改
		//	strcpy(_str, str);
		//}
		//正确写法:
		/*string(const char* str)
			:_size(strlen(str))
		{
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);

		}*/

		//最后:将不带参的构造函数和代参的构造函数合二为一
		string(const char* str = nullptr)
			:_size(strlen(str))
		{
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);

		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};

	void test_string1()
	{
		string s1("hello world");
		string s2;
	}

}

以上代码还存在着错误:

因为空的string在这里存在着问题,会导致程序出现崩溃:

string(const char* str = nullptr)
            :_size(strlen(str)) //在这里崩掉
        {
            _capacity = _size;
            _str = new char[_capacity + 1];
            strcpy(_str, str);

        }

string(const char* str = ‘\0’) //这里也存在着问题,由于语法会报错:这是一个字符自变量char,而我们定义的是char*
            :_size(strlen(str)) 
        {
            _capacity = _size;
            _str = new char[_capacity + 1];
            strcpy(_str, str);

        }

string(const char* str = "\0") //等于一个字符串正确了,但是相当于有两个\0
            :_size(strlen(str)) 
        {
            _capacity = _size;
            _str = new char[_capacity + 1];
            strcpy(_str, str);

        }

正确写法:

        string(const char* str = "") //或者直接空串(包含\0)
            :_size(strlen(str)) 
        {
            _capacity = _size;
            _str = new char[_capacity + 1];
            strcpy(_str, str);

        }

非要分开写时   :_str(nullptr) 这样的写法存在潜在的问题:

    string()
        :_str(nullptr)
        , _size(0)
        , _capacity(0)
    {
    }

    string(const char* str)
        :_size(strlen(str))
    {
        _capacity = _size;
        _str = new char[_capacity + 1];
        strcpy(_str, str);

    }

    const char* c_str()
    {
        return _str;
    }
private:
    char* _str;
    size_t _size;
    size_t _capacity;
};

void test_string1()
{
    string s1("hello world");
    string s2;
    cout << s1.c_str() << endl;

    cout << s2.c_str() << endl;//这句话就会报错

}

因为在c_str()中返回的是一个空指针

需要将这段代码

    string()
        :_str(nullptr)
        , _size(0)
        , _capacity(0)
    {
    }

修改成:

        string()
           :_str(new char[1])
           , _size(0)
           , _capacity(0)
       {
           _str[1] = '\0';
       }

 析构:~string()

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

string 遍历

字符串的大小:size_t size() const

    size_t size() const//普通对象可以调用,const对象也可以调用
    {
        return _size;
    }

容量大小:size_t capacity() const

size_t capacity() const
{
    return _capacity;
}

char& operator[](size_t pos)是提供对字符串中特定位置字符的访问,并且允许对这个字符进行读写操作。

  • 引用的好处

    • 可读可写:通过返回引用,可以直接修改 _str[pos] 的值。例如:

      cpp复制

      s[0] = 'H'; // 修改字符串的第一个字符
    • 高效:返回引用避免了不必要的拷贝,直接操作原始数据。

   /*ReturnType& operator[](int index);
    ReturnType:返回类型,通常是类中元素的引用,以便支持修改操作。
    index:下标参数,表示要访问的元素位置。*/

    char& operator[](size_t pos)
    {
        assert(pos < _size);
/*检查是否越界!!!很有用!!!,一般越界读检查不出来,越界写是抽查*/


        //为什么可以返回呢?
        //因为用的引用,返回的是别名

        return _str[pos];
    /*_str:成员变量,_str[]:解引用所指向的空间在堆上,
        出了作用域还在,返回的是pos位置上的字符,
        从而用引用返回,而不是拷贝,而是别名,因此可读可写*/

通过所讲的构造、遍历、析构函数运行此函数:

void test_string1()
{
    //可写
    for (size_t i = 0; i < s1.size(); i++)
    {
        s1[i]++;
    }
    cout << endl;
    //可读
    for (size_t i = 0; i < s1.size(); i++)
    {
        cout << s1[i] << " ";
    }
    cout << endl;
}

通过这个测试实例,我们了解到operator[]()还需要完善,不能直接同size()函数一样在后面加const

const string s3("xxx");
for (size_t i = 0; i < s1.size(); i++)
{
    cout << s3[i] << " ";
//报错原因,const对象不能调用非const成员函数
}
cout << endl;

通过阅读学习文档:则我们写一个常量访问的重载就可

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

//const对象是只读的

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

运行结果:

迭代器iterator _ 范围for

typedef char* iterator;
    iterator begin()
    {
        return _str;
    }
    iterator end()
    {
        return _str + _size;
    }

 string中大致类似图示:(注意不是所有容器的迭代器的实现都是指针,比如说链表的迭代器)

vs下的string迭代器(第一排代码),第二排是我的代码作用域中所使用的:

运行结果:

当前情况下的迭代器实现类似:

为了满足常量访问:

    typedef const char* const_iterator;
    const_iterator begin() const
    {
        return _str;
    }
    const_iterator end() const
    {
        return _str + _size;
    }

测试函数:   

 void test_string2()
    {
        string s3("hello world");
     
   //迭代器
        string::iterator it3 = s3.begin();
        while (it3 != s3.end())
        {
            //*it3 -= 3;
            cout << *it3 << " ";
            ++it3;
        }
        cout << endl;

        //自动取值迭代,编译底层类似于迭代器的代码,将*s3赋值给ch
        for (auto ch : s3)
        {
            cout << ch << " ";
        }
        cout << endl;

        //const迭代器
        string s4("ncjaskcja");
        string::const_iterator it4 = s4.begin();
        while (it4 != s4.end())
        {
            cout << *it4 << " ";
            ++it4;
        }

        cout << endl;
        for (auto ch : s4)
        {
            cout << ch << " ";
        }
        cout << endl;

    }

运行结果:

增删查改

	void test_string3()
	{
		string s3("hello");
		s3.push_back('1');
		cout << s3.c_str() << endl;
		s3.append("hello");
		cout << s3.c_str() << endl;
	}

实现这两个:



需要为append和push_back,提供一个reserve函数扩容

reserve函数:只扩容,不缩容,扩容的量比当前的capacity大的时候才会扩容

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

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

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

	strcpy(_str + _size, str);

	_size += len;
}

对代码的解释:

append 要多少扩容多少,push_back扩容两倍

strcpy(_str + _size, str);

  • _str 是自定义字符串类 bit::string 的内部存储空间,用于存储字符串数据。

  • _size 是当前字符串的长度(不包括终止符 \0)。

  • _str + _size 表示 _str 中当前字符串的末尾位置(即第一个空闲位置)。

  • str 是要追加的字符串。

reserve函数:c++是没有malloc realloc的,扩容很复杂涉及到深浅拷贝等问题

		void reserve(size_t n)//扩容n:库里的功能:当前空间大就不扩,小就扩
		{
			if (n > _capacity)//为什么要写,reserve单独使用的情况
			{
				char* tmp = new char[n+1];//+1为'\0'预留空间
				strcpy(tmp, _str);//将值拷贝到新空间
				delete[] _str;//释放旧空间
				_str = tmp;//让指针指向新空间
				_capacity = n;
			}
		}

拼接:string& operator+=(char ch)

string& operator+=(char ch)  //拼接字符
{
	push_back(ch);
	return *this;
}
string& operator+=(const char* str)  //拼接字符串
{
	append(str);
	return *this;
}

测试:

string s3("hello");
s3 += 'x';
cout << s3.c_str() << endl;
s3 += "wuwuwuuw";
cout << s3.c_str() << endl;

测试结果:

插入字符 insert

思路:

1.对插入的位置pos进行检查,小于等于字符串的大小_size

等于的时候是尾插:

	assert(pos <= _size);

2.扩容:同push_back

3.赋值:

    void insert(size_t pos, char ch)
    {
        //检查pos
        assert(pos <= _size);
        //扩容n倍
        if (_size == _capacity)
        {
            //reserve(2 * _capacity);如果_capacity == 0?
            reserve(_capacity == 0 ? 4 : 2 * _capacity);
        }

        size_t end = _size;//size_t等一下更改为int
        while (end >= pos)//当end>=pos就可以将pos后的字符继续向后移动
        {
            _str[end + 1] = _str[end];
            --end;
        }
        _str[pos] = ch;
        ++_size; 
    }

问题:当pos = 0时,运行到--end时,end = -1 ,  但 size_t 不存在 < 0,所以我们更改end的类型为int

而此时却再次进入循环:

赋值后:

陷入死循环:

为什么?

一个运算符如果当两边的的操作数类型不同时,会发生类型提升,范围小的类型会像范围大的提升,所以int 还是被提升为size_t,无符号数,最后发生越界。

方法:

1.强转

在库里:

(因为下标不可能是一个负数,所以用size_t)

图解:

插入字符串 insert

void insert(size_t pos,const char* str)
{
	//检查pos
	assert(pos <= _size);
	//扩容
	 
	size_t len = strlen(str); //记录需要插入的字符串的长度
	cout << len << endl;

	if (_size + len > _capacity)
	{
		reserve(_size + len);
	}

	size_t end = _size + len;//由插入后整个字符串的长度,得到end的位置

	//
	while (end > pos + len - 1) 
	{
		_str[end] = _str[end - len];
		--end;
	}
	
	strncpy(_str + pos, str, len);
	_size += len;

}//作业

删除字符串

在库中

std::string 中的 erase()

std::string 也提供了 erase 函数,用法与顺序容器类似:

删除单个字符
iterator erase(iterator position);
  • 参数position 是一个迭代器,指向要删除的字符。

  • 返回值:返回一个指向被删除字符之后字符的迭代器。

  • 示例

    std::string str = "Hello";
    auto it = str.begin() + 1; // 指向字符 'e'
    str.erase(it); // 删除字符 'e'
    // str 现在为 "Hllo"
删除范围内的字符
iterator erase(iterator first, iterator last);
  • 参数first 和 last 是迭代器,表示要删除的字符范围 [first, last)

  • 返回值:返回一个指向被删除范围之后字符的迭代器。

  • 示例

    std::string str = "Hello";
    auto it1 = str.begin() + 1; // 指向字符 'e'
    auto it2 = str.begin() + 4; // 指向字符 'o'
    str.erase(it1, it2); // 删除字符 'e', 'l', 'l'
    // str 现在为 "Ho"
删除从指定位置开始的多个字符
basic_string& erase(size_type pos = 0, size_type count = npos);
  • 参数

    • pos:要删除的起始位置。

    • count:要删除的字符数。如果 count 为 npos(默认值),则删除从 pos 开始到字符串末尾的所有字符。

  • 返回值:返回修改后的字符串。

  • 示例

    std::string str = "Hello, World!";
    str.erase(7, 5); // 从位置 7 开始删除 5 个字符
    // str 现在为 "Hello, !"

模拟实现:

        void erase(size_t pos, size_t len = npos)  

        //从pos位置开始删,删除len个 ,npos默认值 = -1
        {
            assert(pos < _size);
//_str[_size]的位置是'\0'
             
          
 //1.当len = npos 或者pos + len 已经大于整个字符串大小的时候,就将后面的都删除
            // if (len == npos ||pos + len >= _size)

            //但这里存在问题 pos + len有可能溢出,npos = -1,实际上是整形最大值,所以这种方法还需要需改
            if (len == npos ||len >= _size - pos) 
            {
                _str[pos] = '\0';
                _size = pos;
            }
         
   //2.在中间删除
            //思路,用后边的字符来覆盖要删除的部分,再加'\0'

            else
            {
                strcpy(_str + pos, _str + pos + len);
/*用strcpy的好处在于,当拷贝到'\0'时,自动结束
                也可以用strncpy,但是需要计算出拷贝的数量*/

                _size -= len;
            }

        }

缩减字符串的长度:resize

库:

模拟实现

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++)
//扩容,那么就从原始长度的下一个开始进行插入'\0'就是_size,到新长度n结束
        {
            _str[i] = ch;
        }
        _str[n] = '\0'; //记得结尾标志
        _size = n;
    }
}

拷贝构造函数(传统写法):string(const string& s)

在执行下面的代码中,我们的程序会出现报错。

是因为,这里使用的是程序自动生成的拷贝构造函数,只进行对s1的浅拷贝(值拷贝),在前面的文章有细致讲解过类似的简单浅拷贝深拷贝。而析构时析构两次统一空间,导致的程序报错。

所以我们需要做一个深拷贝:

        string(const string& s)
    {
      
 //给需要拷贝值的对象开辟和被拷贝对象相同的空间
        _str = new char[s._capacity + 1]; //注意又给'\0'预留了空间
        //拷贝数据
        strcpy(_str, s._str);
        _size = s._size;
        _capacity = s._capacity;
    }

监视观察:

s1和s2各自有各自的空间。

赋值:string& operator=(const string& s)

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;
}

交换:swap()

void test_string6() 
{ 
	string s1("hello world");
	cout << s1.c_str() << endl;

	s1.insert(5, "123");
	cout << s1.c_str() << endl;

	string s2("xxxxxxx");
	cout << s2.c_str() << endl;

	swap(s1, s2);
	cout << s1.c_str() << endl;
	cout << s2.c_str() << endl;
}

这里的拷贝是深拷贝,这里一共就是三次拷贝加一次析构;

因此写一个swap成员函数:

void swap(string& s)
{
	std::swap(_str, s._str); //注意不要直接写swap(),会在局部找,得指定区域
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}

使用时:

那么有什么办法能避免直接使用swap:

我们再写一个这样的成员函数swap,通过这样,在有人直接使用swap(x,y)时,实际上调用的是我们自己写的更加高效的swap。

找寻字符和字符串位置:find()

怎么得到下标,得到两个位置的指针再相减就是下标。

		size_t find(const char* sub, size_t pos = 0) const   //找寻字符串位置
		{
			assert(pos < _size);

			//思路:做子串的匹配
			//1.strstr()暴力匹配
			const char* p = strstr(_str, sub);
			if (p)
			{
				return p - _str;
			}
			else
			{
				return npos;
			}
			//2.kmp算法(比特大博哥

		}

从字符串中提取子字符串:substr

在C++中,std::string 类提供了一个成员函数 substr,用于从字符串中提取子字符串。substr 函数的基本语法如下:

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

参数说明:

  1. pos:

    • 表示子字符串的起始位置(从0开始计数)。

    • 默认值为 0,即从字符串的开头开始。

  2. len:

    • 表示要提取的子字符串的长度。

    • 默认值为 std::string::npos,这是一个特殊值,表示从 pos 开始到字符串的末尾。

返回值:

  • 返回一个新的 std::string 对象,包含从 pos 开始的 len 个字符。

模拟实现:

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

			if (len == npos || len >= _size - pos) //len == npos也是在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;
		} 

判断大小:operator== ,>,>=,<,<=,!= 

这里我们需要用到strcmp,需要知道返回值:在这里,如果相等则返回 0 


		bool operator==(const string& s)
		{
			int ret = strcmp(_str, s._str); 
			return ret == 0;
		}

当我们这样写之后:

第三个支持的原因是因为,单参数的构造函数支持隐式类型的转换。

这里的const char*("hello world")  可以转换为 string,因为在上面我们有写一个单参数的构造函数

支持用一个const char* 来构造一个string

而第二种就不行:

出现这样的错误提示:

因为我们的bool operator==(const string& s)  是一个成员函数,成员函数的左边必须是对象,对象才能调用成员函数。

所以在operator==这里不能写成成员函数,得重载成一个全局的

这里出现的原因是前面写模拟c_str()函数时,没有加const:现在加上

运行结果:

由此我们直接复用就可以得出接下来的大小比较的代码:


	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)
	{
		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)  //将提取的值放到string里
    {
        char ch;
     
   //in << s[i];能直接这样吗 写个for循环,不可以,
        // 因为在这里s的空间都没开好,无法写入数据

        in >> ch;
        while (ch != ' ' && ch != '\n')
 //结束标志,等于空格等于换行(getline、in.get(ch)可以解决这种问题)
        {
            s += ch;
            in >> ch;
        }

        return in;
    }

我们现在测试:

测试时发现,程序陷入死循环:

调试时就会发现,明明代码中写了在有空格和换行时退出循环,但是这里却直接从o 跳到 z,忽略了空格,我们又输入了hh,调试信息中显示,h h -->又到h,忽略了换行,这是为什么?

因为c++的 cin 和 c 的 scanf 读字符的时候根本取不到 、忽略空格和换行,默认是多个字符之间的分割。

所以我们的流提取代码还有问题。

在c语言中可以用getchar(),和getc()来解决,但是c++我们用流提取的时候不行。因为c语言的io流,和c++的io流是不一样的,各自有各自的缓冲区(类似于流提取自己会把他的数据给提取进来到c++的内存里,而c语言读取到c语言的内存中)。

内置类型能直接支持是因为,库里面把内置类型直接重载了,能自动识别识别类型,匹配类型

 所以我们修改流提取代码:

    istream& operator<<(istream& in, string& s)  //将提取的值放到string里
    {
        char ch;
     
   //in << s[i];能直接这样吗 写个for循环,不可以,
        // 因为在这里s的空间都没开好,无法写入数据

        //in >> ch;

        ch = in.get(); //是一个字符就直接取,不会忽略分隔符
        while (ch != ' ' && ch != '\n')
 //结束标志,等于空格等于换行(getline、in.get(ch)可以解决这种问题)
        {
            s += ch;
            ch = in.get();
        }

        return in;
    }

而此时还存在问题,问题在于我们的s1 、s2不是空string,这里的+=相当于尾插,而流提取是覆盖;所以我们需要先清除string对象。

在进入函数时,添加一个清除函数:clear()。 

主要是将字符串的内容清除为0,即size = 0,capacity可以不变。

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

 修改后正确的流提取函数:

	istream& operator>>(istream& in, string& s)  //将提取的值放到string里
	{
		s.clear();
		char ch;
		//in << s[i];能直接这样吗 写个for循环,不可以,
		// 因为在这里s的空间都没开好,无法写入数据
		//in >> ch;
		ch = in.get(); //是一个字符就直接取,不会忽略分隔符
		while (ch != ' ' && ch != '\n')  //结束标志,不等于空格不等于换行(getline、in.get(ch);可以解决这种问题)
		{
			s += ch;
			//in >> ch;

			ch = in.get();
			
		}
		return in;
	}

提取行getline()

istream& getline(istream& in, string& s)
{
	s.clear();
	char ch;
	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;
}

照如图所示的思路:

记得要初始化s2

拷贝构造函数更新(新写法、现代写法):

库里面不一定会处理,但是我们这里有处理,为了安全所以最好还是要写上

因此我们在声明变量的时候先添加缺省值

赋值运算符的现代写法 

		string& operator=(string ss)
		{
			swap(ss);
			return *this;
		}

计算大小,为什么?

答案是 :28

               12    

结语:

       随着这篇关于题目解析的博客接近尾声,我衷心希望我所分享的内容能为你带来一些启发和帮助。学习和理解的过程往往充满挑战,但正是这些挑战让我们不断成长和进步。我在准备这篇文章时,也深刻体会到了学习与分享的乐趣。

       在此,我要特别感谢每一位阅读到这里的你。是你的关注和支持,给予了我持续写作和分享的动力。我深知,无论我在某个领域有多少见解,都离不开大家的鼓励与指正。因此,如果你在阅读过程中有任何疑问、建议或是发现了文章中的不足之处,都欢迎你慷慨赐教。         你的每一条反馈都是我前进路上的宝贵财富。同时,我也非常期待能够得到你的点赞、收藏,关注这将是对我莫大的支持和鼓励。当然,我更期待的是能够持续为你带来有价值的内容,让我们在知识的道路上共同前行。

相关文章:

  • Eureka、ZooKeeper 和 Nacos 之间的对比
  • YOLO11改进-模块-引入混合结构模块Mix Structure Block 提高多尺度、小目标
  • 使用Windbg调试目标进程排查C++软件异常的一般步骤与要点分享
  • 6层高速PCB设计入门第1~10讲
  • STM32CUBEIDE FreeRTOS操作教程(十三):task api 任务访问函数
  • 原生稀疏注意力NSA 替换transformer 注意力进行文本生成训练
  • Web自动化之Selenium添加网站Cookies实现免登录
  • C++ ——— 二叉搜索树
  • EasyExcel 使用指南:基础操作与常见问题
  • MySQL 最左前缀原则:原理、应用与优化
  • Winform工具箱、属性、事件
  • 04基于vs2022的c语言笔记——数据类型
  • C# httpclient 和 Flurl.Http 的测试
  • Mesh自组网技术及应用
  • Threejs教程三【揭秘3D贴图魔法】
  • 如何使用爬虫获取淘宝商品详情:API返回值说明与案例指南
  • Unity 第三人称人物切动画时人物莫名旋转
  • 3.18 ReAct 理论实战:构建动态推理-行动循环的企业级 Agent
  • pycharm技巧--鼠标滚轮放大或缩小 Pycharm 字体大小
  • ESP8266+STM32+阿里云保姆级教程(AT指令+MQTT)
  • c 语言做网站/北京网站开发
  • 网站建设需要什么手续/友情链接导航
  • 怎样做聊天网站/长沙seo研究中心
  • 做诈骗网站/合肥今日头条最新消息
  • 公司做网站要注意什么/宁波网络营销怎么做
  • 广告网站建设流程/百度seo搜索营销新视角