【C++】String类的模拟实现。

简介: 【C++】String类的模拟实现。

模拟实现

成员变量

string类中需要三个成员变量分别记录元素个数、容量和内容。还需要一个size_t类型npos-1表示整型的最大值。

private:
    size_t _size;//个数
    size_t _capacity;//容量
    char* _str;//字符数组
public:
    const static size_t npos;
const size_t string::npos = -1;//npos代表的整型的最大值。

构造函数

//构造函数
string(const char* str = "")
{
  _size = strlen(str);
  _capacity = _size;
  _str = new char[_capacity + 1];
  memcpy(_str, str, _size + 1);
}

这段代码是string类的构造函数。构造函数是在创建对象时自动调用的函数,用于初始化对象的数据成员。

这个构造函数接受一个const char*类型的参数str,默认值为一个空字符串。

在函数体内,首先使用strlen函数获取参数str的长度,并将其赋值给私有成员变量_size,表示字符串的个数。

接着,将_capacity的值初始化为_size,表示容量与个数相等。

然后,使用new运算符动态分配一个大小为_capacity + 1的字符数组,并将指针赋值给私有成员变量_str。

最后,使用memcpy函数将参数str中的字符串复制到新分配的字符数组中,并在最后一个位置设置为\0,表示字符串的结束。

因此,这个构造函数的作用是将一个C风格的字符串转换为string对象。

析构函数

//析构函数
~string()
{
  delete[] _str;
  _str = nullptr;
  _size = _capacity = 0;
}

这段代码是string类的析构函数。析构函数是在对象被销毁时自动调用的函数,用于释放对象占用的资源。

在函数体内,首先使用delete[]运算符释放_str指向的动态分配的字符数组。

然后,将_str赋值为nullptr,表示指针不再指向任何内存地址。

最后,将_size和_capacity的值都设为0,表示对象已经被销毁。

因此,这个析构函数的作用是释放string对象占用的内存空间,并将对象的数据成员重置为初始值。

拷贝构造

//拷贝构造
string(const string& str)
{
  _str = new char[str._capacity + 1];
  memcpy(_str, str._str, str._size + 1);
  _size = str._size;
  _capacity = str._capacity;
}

这段代码是string类的拷贝构造函数。拷贝构造函数是在创建一个新对象并用已有对象进行初始化时自动调用的函数,用于将一个对象的值复制到另一个对象中。

这个拷贝构造函数接受一个const string&类型的引用参数str,表示要被复制的对象。

在函数体内,首先使用new运算符动态分配一个大小为str._capacity + 1的字符数组,并将指针赋值给私有成员变量_str。

然后,使用memcpy函数将参数str中的字符串复制到新分配的字符数组中,并在最后一个位置设置为\0,表示字符串的结束。

接着,将_size和_capacity的值分别赋值为参数str对应的私有成员变量值,表示新对象的大小和容量与原对象相同。

因此,这个拷贝构造函数的作用是创建一个新的string对象,并将参数str中的数据成员值复制到新对象中。

c_str()

//返回C语言类型的字符串
const char* c_str() const
{
  return _str;
}

这段代码是string类的c_str函数,用于返回一个指向以空字符结尾的字符数组的指针,该字符数组包含了当前string对象中存储的字符串。

这个函数没有参数,返回值为const char*类型。

在函数体内,直接返回私有成员变量_str,该变量是一个指向字符数组的指针,指向当前string对象中存储的字符串。

由于返回值是一个指向常量字符的指针,因此该函数被声明为const类型成员函数,表示该函数不会修改对象的数据成员。

因此,这个函数的作用是返回当前string对象中存储的字符串的C语言类型表示。

size()

//返回长度
size_t size() const
{
  return _size;
}

这段代码是string类的size函数,用于返回当前string对象中存储的字符串的长度,即字符的个数。

这个函数没有参数,返回值为size_t类型。

在函数体内,直接返回私有成员变量_size,该变量表示当前string对象中存储的字符串的长度。

由于该函数不会修改对象的数据成员,因此被声明为const类型成员函数。

因此,这个函数的作用是返回当前string对象中存储的字符串的长度。

重载[]运算符

//重载[]运算符
char& operator[](size_t pos)
{
  assert(pos < _size);
  return _str[pos];
}
const char& operator[](size_t pos) const
{
  assert(pos < _size);
  return _str[pos];
}

这段代码是string类对[]运算符进行重载的实现。通过重载[]运算符,可以使用类似数组下标的方式访问和修改string对象中的字符。

这个代码片段包含了两个重载版本,一个是非常量版本,另一个是常量版本。

在非常量版本中,重载的[]运算符接受一个size_t类型的参数pos,表示要访问或修改的字符的索引位置。

在函数体内,首先使用assert函数来检查索引位置是否越界,即是否小于字符串的长度_size。

然后,返回私有成员变量_str中索引位置为pos的字符的引用,以便可以修改该字符的值。

在常量版本中,重载的[]运算符也接受一个size_t类型的参数pos。

在函数体内,同样使用assert函数来检查索引位置是否越界。

然后,返回私有成员变量_str中索引位置为pos的字符的常量引用,以保证该字符的值不会被修改。

因此,这段代码的作用是重载string类的[]运算符,使得可以通过下标访问和修改字符串中的字符。

扩容函数

reserve()

//扩容函数
void reserve(size_t n)
{
  if (n > _capacity)
  {
    char* tmp = new char[n + 1];
    memcpy(tmp, _str, _size+1);
    delete[] _str;
    _str = tmp;
    _capacity = n;
  }
}

这段代码是string类的reserve函数,用于增加当前string对象的容量,以便可以存储更多的字符。

这个函数接受一个size_t类型的参数n,表示要增加的容量大小。

在函数体内,首先判断参数n是否大于当前对象的容量_capacity。如果是,则需要进行扩容操作。

在扩容操作中,首先使用new运算符动态分配一个大小为n+1的字符数组,并将指针赋值给临时变量tmp。

然后,使用memcpy函数将当前对象中的字符串复制到新分配的字符数组中,这个函数比strcpy更快,因为不需要在找到\0之后继续复制。

接着,使用delete[]运算符释放当前对象中原来的字符数组。

最后,将指向新分配的字符数组的指针赋值给私有成员变量_str,表示当前对象已经扩容。同时,将私有成员变量_capacity的值更新为新的容量大小。

因此,这个函数的作用是增加当前string对象的容量,以便可以存储更多的字符。

resize()

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

这段代码是string类的resize函数,用于改变当前string对象中存储的字符串的长度。

这个函数接受一个size_t类型的参数n,表示要改变的字符串长度。同时,还接受一个可选的char类型参数ch,表示在扩展字符串时要填充的字符,默认值为\0。

在函数体内,首先判断参数n是否小于当前对象中存储的字符串的长度_size。如果是,则需要缩小字符串的长度。

在缩小字符串长度的操作中,首先将私有成员变量_size的值更新为n,表示当前对象中存储的字符串的长度已经缩小到了n。

然后,在新的字符串长度之后添加一个空字符\0,以便可以正确地表示新的字符串。

如果参数n大于等于当前对象中存储的字符串的长度_size,则需要扩展字符串的长度。

在扩展字符串长度的操作中,首先调用成员函数reserve(n)来确保当前对象中分配的内存空间足够存储新的字符串。

然后,在当前对象中存储的字符串的末尾添加字符ch,直到新字符串的长度达到了n。

最后,将私有成员变量_size的值更新为n,表示当前对象中存储的字符串的长度已经扩展到了n。同时,在新字符串长度之后添加一个空字符\0,以便可以正确地表示新的字符串。

因此,这个函数的作用是改变当前string对象中存储的字符串的长度,并根据需要在新字符串中添加或删除字符。

尾插

push_back()

//push_back()
void push_back(char ch)
{
  if (_size == _capacity)
  {
    reserve(_capacity == 0 ? 4 : 2 * _capacity);
  }
  _str[_size] = ch;
  ++_size;
  _str[_size] = '\0';
}

这段代码是string类的push_back函数,用于向当前string对象中添加一个字符。

这个函数接受一个char类型的参数ch,表示要添加的字符。

在函数体内,首先判断当前对象中存储的字符串的长度_size是否等于当前对象的容量_capacity。如果是,则需要进行扩容操作。

在扩容操作中,调用成员函数reserve(_capacity == 0 ? 4 : 2 * _capacity)来增加当前对象的容量。如果当前对象的容量为0,则增加容量到4;否则,将容量增加到原来的2倍。

然后,在私有成员变量_str中索引位置为_size的位置添加字符ch,表示将该字符添加到当前对象的末尾。

接着,将私有成员变量_size的值加1,表示当前对象中存储的字符串的长度已经增加了1个字符。

最后,在新字符串长度之后添加一个空字符\0,以便可以正确地表示新的字符串。

因此,这个函数的作用是向当前string对象中添加一个字符,并在需要时自动扩容以确保有足够的空间存储该字符。

append()

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

这段代码是string类的append函数,用于将一个以空字符结尾的C字符串附加到当前string对象中。

这个函数接受一个const char*类型的参数str,表示要附加的C字符串。

在函数体内,首先使用strlen函数计算出C字符串的长度,并将其存储在临时变量len中。

然后,判断将要附加的C字符串的长度加上当前对象中存储的字符串的长度是否大于等于当前对象的容量。如果是,则需要进行扩容操作。

在扩容操作中,调用成员函数reserve(len + _size)来增加当前对象的容量,以确保有足够的空间存储将要附加的C字符串。

接着,使用memcpy函数将C字符串复制到当前对象中存储的字符串的末尾。这个函数比strcpy更快,因为不需要在找到\0之后继续复制。

最后,将私有成员变量_size的值增加C字符串的长度len,表示当前对象中存储的字符串的长度已经增加了C字符串的长度。

因此,这个函数的作用是将一个以空字符结尾的C字符串附加到当前string对象中,并在需要时自动扩容以确保有足够的空间存储该字符串。

重载+=运算符

.

//重载+=运算符
string& operator+=(char ch)
{
  push_back(ch);
  return *this;
}
string& operator+=(const char* str)
{
  append(str);
  return *this;
}

这里都是都是复用了上面的push_back()和append()

插入函数insert

插入字符

//在指定位置插入n个字符
void insert(size_t pos, size_t n, char ch)
{
  assert(pos <= _size);
  if (_size + n > _capacity)
  {
    reserve(_size + n);
  }
  size_t end = _size;
  while (end >= pos && end != npos)//npos整型最大值
  {
    _str[end + n] = _str[end];
    --end;
  }
  for (size_t i = pos; i < pos + n; i++)
  {
    _str[i] = ch;
  }
  _size += n;
}

这段代码是string类的insert函数,用于在当前string对象中的指定位置插入指定数量的字符。

这个函数接受三个参数:一个size_t类型的参数pos,表示要插入字符的位置;一个size_t类型的参数n,表示要插入的字符数量;一个char类型的参数ch,表示要插入的字符。

在函数体内,首先使用assert函数检查要插入的位置是否在当前对象中存储的字符串的范围内。如果不在范围内,则程序会终止。

然后,判断将要插入的字符数量加上当前对象中存储的字符串的长度是否大于等于当前对象的容量。如果是,则需要进行扩容操作。

在扩容操作中,调用成员函数reserve(_size + n)来增加当前对象的容量,以确保有足够的空间存储将要插入的字符。

接着,从当前对象中存储的字符串的末尾开始,将每个字符向后移动n个位置。这个操作是为了给将要插入的字符腾出空间。

然后,在指定位置插入n个字符。这个操作使用了一个for循环,从pos开始,循环n次,每次将要插入的字符添加到当前对象中存储的字符串中。

最后,将私有成员变量_size的值增加n,表示当前对象中存储的字符串的长度已经增加了n个字符。

需要注意的是,在while循环中使用了变量npos,这个变量是一个常量,表示无效的位置。在这个函数中,它被用来表示当前对象中存储的字符串已经被遍历完了。

因此,这个函数的作用是在当前string对象中指定位置插入指定数量的字符,并在需要时自动扩容以确保有足够的空间存储这些字符。

插入字符串

//在指定位置插入一个字符串
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;
  while (end >= pos && end != npos)//npos表示整型最大值
  {
    _str[end + len] = _str[end];
    --end;
  }
  for (size_t i = pos; i < pos + len; i++)
  {
    _str[i] = str[i - pos];
  }
  _size += len;
}

这段代码是string类的insert函数,用于在当前string对象中的指定位置插入一个C风格字符串。

该函数首先检查要插入的位置是否在当前对象中存储的字符串的范围内,然后计算要插入的字符串长度。如果将要插入的字符数量加上当前对象中存储的字符串的长度大于等于当前对象的容量,则需要进行扩容操作。

在扩容操作中,调用成员函数reserve(_size + len)来增加当前对象的容量,以确保有足够的空间存储将要插入的字符串。

接着,从当前对象中存储的字符串的末尾开始,将每个字符向后移动len个位置。这个操作是为了给将要插入的字符串腾出空间。

然后,在指定位置插入字符串。这个操作使用了一个for循环,从pos开始,循环len次,每次将要插入的字符添加到当前对象中存储的字符串中。

最后,将私有成员变量_size的值增加len,表示当前对象中存储的字符串的长度已经增加了len个字符。

erase

//指定位置删除n个字符
void erase(size_t pos, size_t len = npos)
{
  assert(pos <= _size);
  if (len == npos || pos + len >= _size)
  {
    _str[pos] = '\0';
    _size = pos;
    _str[_size] = '\0';
  }
  else
  {
    size_t end = pos + len;
    while (end <= _size)
    {
      _str[pos++] = _str[end++];
    }
    _size -= len;
  }
}

这段代码是string类的erase函数,用于从当前string对象中的指定位置开始删除指定数量的字符。

这个函数接受两个参数:一个size_t类型的参数pos,表示要删除字符的起始位置;一个size_t类型的参数len,表示要删除的字符数量,其默认值为npos,表示删除从起始位置到当前对象末尾的所有字符。

在函数体内,首先使用assert函数检查要删除的位置是否在当前对象中存储的字符串的范围内。如果不在范围内,则程序会终止。

然后,判断要删除的字符数量是否等于npos或者要删除的字符数量加上要删除的起始位置是否大于等于当前对象中存储的字符串的长度。如果是,则将当前对象中存储的字符串从指定位置开始到末尾的所有字符都删除,并将当前对象中存储的字符串的长度更新为指定位置。

否则,从指定位置开始,将要删除的字符向后移动len个位置。这个操作使用了一个while循环,从pos开始,循环到pos+len结束,每次将要删除的字符向后移动一个位置。

最后,将私有成员变量_size的值减去len,表示当前对象中存储的字符串的长度已经减少了len个字符。

需要注意的是,在if语句中,将pos位置处的字符设置为’\0’,是为了在删除操作后保证当前对象中存储的字符串以’\0’结尾。

查找函数find

查找字符

//在指定位置查找一个字符
size_t find(char ch, size_t pos = 0)
{
  assert(pos < _size);
  for (size_t i = pos; i < _size; i++)
  {
    if (_str[i] == ch)
    {
      return i;
    }
  }
  return npos;
}

这段代码是string类的find函数,用于在当前string对象中的指定位置开始查找一个字符。

这个函数接受两个参数:一个char类型的参数ch,表示要查找的字符;一个size_t类型的参数pos,表示从哪个位置开始查找,其默认值为0。

在函数体内,首先使用assert函数检查要查找的起始位置是否在当前对象中存储的字符串的范围内。如果不在范围内,则程序会终止。

然后,使用一个for循环从指定位置开始遍历当前对象中存储的字符串。如果遍历到的字符等于要查找的字符,则返回该字符在当前对象中存储的字符串中的位置。

如果整个字符串都被遍历完了,还没有找到要查找的字符,则返回常量npos,表示未找到。

需要注意的是,在这个函数中,变量npos表示整型最大值,被用来表示未找到要查找的字符。


查找字符串

//在指定位置查找一个字符串
size_t find(const char* str, size_t pos = 0)
{
  assert(pos < _size);
  const char* ptr = strstr(_str + pos, str);
  if (ptr)
  {
    return ptr - _str;
  }
  else
  {
    return npos;
  }
}

这段代码是string类的find函数,用于在当前string对象中的指定位置开始查找一个字符串。

这个函数接受两个参数:一个const char*类型的参数str,表示要查找的字符串;一个size_t类型的参数pos,表示从哪个位置开始查找,其默认值为0。

在函数体内,首先使用assert函数检查要查找的起始位置是否在当前对象中存储的字符串的范围内。如果不在范围内,则程序会终止。

然后,使用C标准库函数strstr在当前对象中存储的字符串中查找要查找的字符串。这个函数返回一个指向第一次出现要查找的字符串的指针,如果没找到则返回NULL。

如果找到了要查找的字符串,则返回该字符串在当前对象中存储的字符串中的位置。由于strstr函数返回的是指向要查找的字符串在当前对象中存储的字符串中的位置的指针,因此需要将这个指针减去当前对象中存储的字符串的首地址,才能得到该字符串在当前对象中存储的字符串中的位置。

如果整个字符串都被遍历完了,还没有找到要查找的字符串,则返回常量npos,表示未找到。

需要注意的是,在这个函数中,变量npos表示整型最大值,被用来表示未找到要查找的字符串。

substr()

//截取一段字符串
string substr(size_t pos = 0, size_t len = npos)
{
  assert(pos < _size);
  size_t n = len;
  if (len == npos || pos + len > _size)
  {
    n = _size - pos;
  }
  string tmp;
  tmp.reserve(n);
  for (size_t i = pos; i < pos + n; i++)
  {
    tmp += _str[i];
  }
    return tmp;
}

这段代码是string类的substr函数,用于从当前string对象中的指定位置开始获取一个子字符串。

这个函数接受两个参数:一个size_t类型的参数pos,表示子字符串的起始位置,其默认值为0;一个size_t类型的参数len,表示子字符串的长度,其默认值为npos,表示获取从起始位置到当前对象末尾的所有字符。

在函数体内,首先使用assert函数检查要获取子字符串的起始位置是否在当前对象中存储的字符串的范围内。如果不在范围内,则程序会终止。

然后,判断要获取的子字符串的长度是否等于npos或者要获取的子字符串的长度加上要获取的子字符串的起始位置是否大于当前对象中存储的字符串的长度。如果是,则将要获取子字符串的长度设置为从指定位置开始到当前对象末尾的所有字符的长度。

接着,创建一个临时的string对象tmp,并使用reserve函数为这个对象分配足够的内存空间。这个内存空间大小为要获取子字符串的长度。

最后,使用一个for循环从指定位置开始,将要获取的子字符串中的每个字符添加到临时的string对象中。循环结束后,返回临时的string对象。

需要注意的是,在这个函数中,变量npos表示整型最大值,被用来表示要获取从起始位置到当前对象末尾的所有字符。

重载<运算符

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

这段代码是string类的小于运算符重载函数,用于比较当前string对象和另一个string对象的大小关系。

这个函数接受一个const string&类型的参数s,表示要比较的另一个string对象。

在函数体内,使用C标准库函数memcmp比较当前对象中存储的字符串和另一个对象中存储的字符串的前缀部分,比较的长度为两个字符串中长度更短的那个。返回值为0表示两个字符串相等,返回值小于0表示当前对象中存储的字符串小于另一个对象中存储的字符串,返回值大于0表示当前对象中存储的字符串大于另一个对象中存储的字符串。

然后,使用三目运算符判断两个字符串是否相等。如果相等,则比较两个字符串的长度,返回长度更短的那个字符串小于另一个字符串。如果不相等,则返回比较结果。

需要注意的是,在这个函数中,使用了C标准库函数memcmp来进行字符串的比较,而不是使用循环遍历比较每个字符。这是因为memcmp函数可以利用CPU的特殊指令集进行优化,从而提高比较效率。

重载==运算符

bool operator==(const string& s) const
{
  return _size == s._size
    && memcmp(_str, s._str, _size) == 0;
}

这段代码是string类的等于运算符重载函数,用于比较当前string对象和另一个string对象是否相等。

这个函数接受一个const string&类型的参数s,表示要比较的另一个string对象。

在函数体内,首先比较当前对象中存储的字符串的长度和另一个对象中存储的字符串的长度是否相等,如果不相等,则直接返回false,表示两个对象不相等。

然后,使用C标准库函数memcmp比较当前对象中存储的字符串和另一个对象中存储的字符串是否相等。比较的长度为两个字符串中长度更短的那个。返回值为0表示两个字符串相等,返回值不为0表示两个字符串不相等。

最后,使用逻辑运算符&&将前面两个比较的结果合并起来,如果两个比较都为真,则返回true,表示两个对象相等;否则返回false,表示两个对象不相等。

重载<=、>、>=、!=运算符

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

上面这些代码也都是复用了'>'和'=='的代码。

string中的swap函数

void swap(string& s)
{
  std::swap(_str, s._str);
  std::swap(_size, s._size);
  std::swap(_capacity, s._capacity);
}

这段代码是string类的swap函数,用于交换当前string对象和另一个string对象的内容。

这个函数接受一个string&类型的参数s,表示要交换内容的另一个string对象。

在函数体内,使用C++标准库函数std::swap来交换当前对象中存储的字符串和另一个对象中存储的字符串、当前对象中存储的字符串的长度和另一个对象中存储的字符串的长度、当前对象中存储的字符串的容量和另一个对象中存储的字符串的容量。


重载=运算符

第一种重载方法

string& operator=(const string& s)
{
  if (this != &s)
  {
    char* tmp = new char[s._capacity + 1];
    memcpy(tmp, s._str, s._size+1);
    delete[] _str;
    _str = tmp;
    _size = s._size;
    _capacity = s._capacity;
  }
  return *this;
}

第二种重载方法

string& operator=(const string& s)
{
  if (this != &s)
  {
    string tmp(s);
    swap(tmp);
  }
  return *this;
}

第三种重载方法

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

迭代器

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类的迭代器相关函数,用于提供对string对象中存储的字符串字符的访问。

在这个代码中,首先使用typedef关键字定义了两个类型别名iterator和const_iterator,分别表示可变迭代器和不可变迭代器。可变迭代器可以用于修改string对象中存储的字符串,而不可变迭代器则不能。

然后,定义了两个函数begin()和end(),分别返回可变迭代器和不可变迭代器。这两个函数用于返回一个指向当前对象中存储的字符串的首字符的指针和一个指向当前对象中存储的字符串的尾字符的下一个字符的指针。

流插入

ostream& operator<<(ostream& out, const string& s)
{
for (auto ch : s)
{
  out << ch;
}
  return out;
}

这段代码是string类的输出运算符重载函数,用于将当前string对象的内容输出到输出流中。

这个函数接受一个ostream&类型的参数out,表示要输出到的输出流,以及一个const string&类型的参数s,表示要输出的string对象。

在函数体内,使用C++11中的范围for循环遍历当前对象中存储的字符串中的每个字符。对于每个字符,使用输出运算符<<将其输出到输出流中。

最后,返回输出流的引用。

流提取

istream& operator>>(istream& in, string& s)
{
  s.clear();
  char ch = in.get();
  // 处理前缓冲区前面的空格或者换行
  while (ch == ' ' || ch == '\n')
  {
    ch = in.get();
  }
  char buff[128];
  int i = 0;
  while (ch != ' ' && ch != '\n')
  {
    buff[i++] = ch;
    if (i == 127)
    {
      buff[i] = '\0';
      s += buff;
      i = 0;
    }
    ch = in.get();
  }
  if (i != 0)
  {
    buff[i] = '\0';
    s += buff;
  }
  return in;
}

这段代码是一个重载了输入流操作符(>>)的函数,用于从输入流中读取字符串数据。:


1.首先,函数会清空字符串 s,以确保其为空。

2.接下来,使用 in.get() 函数读取输入流中的一个字符,并将其赋值给变量 ch。

3.在一个循环中,检查字符 ch 是否是空格或换行符。如果是,则继续调用 in.get() 读取下一个字符,直到遇到非空格和非换行符的字符为止,这样可以跳过输入流中前面的空格或换行。

4.创建一个字符数组 buff,起初为空。然后,在另一个循环中,将非空格和非换行符的字符存入 buff 中,并逐个增加索引 i。

5.检查索引 i 是否达到了上限(127),即是否已经存满了 buff 数组。如果是,将 buff 数组最后一个字符设置为字符串结束符(‘\0’),并将其内容添加到字符串 s 的末尾。然后,重置索引 i 为0。

6.继续执行循环,直到遇到空格或换行符。在每次循环迭代时,将非空格和非换行符的字符存入 buff 中,并逐个增加索引 i。

7.最后,检查索引 i 是否不为0,即是否有未添加到字符串 s 的字符。如果是,将 buff 数组最后一个字符设置为字符串结束符(‘\0’),并将其内容添加到字符串 s 的末尾。

8.返回输入流 in,以支持链式输入操作。

总体来说,该代码通过循环读取输入流中的字符,并根据空格和换行符的位置将字符拼接成一个字符串。这样,使用该函数可以便捷地从输入流中读取一个带有空格或换行符分隔的字符串。


相关文章
|
6天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
21 2
|
12天前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
40 5
|
19天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
49 4
|
20天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
46 4
|
2月前
|
Java
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
本文深入探讨了Java中方法参数的传递机制,包括值传递和引用传递的区别,以及String类对象的不可变性。通过详细讲解和示例代码,帮助读者理解参数传递的内部原理,并掌握在实际编程中正确处理参数传递的方法。关键词:Java, 方法参数传递, 值传递, 引用传递, String不可变性。
58 1
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
|
2月前
|
安全 Java 测试技术
Java零基础-StringBuffer 类详解
【10月更文挑战第9天】Java零基础教学篇,手把手实践教学!
34 2
|
2月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
28 4
|
2月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
25 4
|
2月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
22 1
|
2月前
|
数据可视化 Java
让星星月亮告诉你,通过反射创建类的实例对象,并通过Unsafe theUnsafe来修改实例对象的私有的String类型的成员属性的值
本文介绍了如何使用 Unsafe 类通过反射机制修改对象的私有属性值。主要包括: 1. 获取 Unsafe 的 theUnsafe 属性:通过反射获取 Unsafe类的私有静态属性theUnsafe,并放开其访问权限,以便后续操作 2. 利用反射创建 User 类的实例对象:通过反射创建User类的实例对象,并定义预期值 3. 利用反射获取实例对象的name属性并修改:通过反射获取 User类实例对象的私有属性name,使用 Unsafe`的compareAndSwapObject方法直接在内存地址上修改属性值 核心代码展示了详细的步骤和逻辑,确保了对私有属性的修改不受 JVM 访问权限的限制
56 4