断言(Assertion)是编程中一种常用的调试辅助手段,用于在代码执行期间验证某个条件是否为真。如果条件为真(即满足预期),则程序继续执行;如果条件为假(即不满足预期),则断言失败,通常会导致程序抛出一个错误、输出一条错误信息,甚至直接终止程序。断言主要用于开发和测试阶段,以确保代码的正确性和健壮性。
assert
宏,定义在<cassert>
(C++)或<assert.h>
(C)头文件中。当NDEBUG
宏未定义时,assert
会检查其条件;如果条件为假,则输出一条错误消息并调用abort
函数终止程序。如果定义了NDEBUG
,则assert
宏被忽略,不产生任何代码。断言的主要目的是帮助开发者在开发和测试阶段发现潜在的问题,确保代码按照预期运行。然而,由于断言会增加额外的运行时开销,并且可能在某些配置下被禁用或忽略,因此它们通常不用于生产环境中的错误处理。相反,生产代码应该使用更健壮的错误处理机制,如异常处理、日志记录和回退策略。
在编写断言时,应该考虑以下几点:
最后,需要注意的是,断言是一种防御性编程技术,但它并不是万能的。开发者还应该使用其他技术和最佳实践来确保代码的质量和可靠性。
在C++中,assert
是一个宏,用于在代码中设置断言。断言是一种调试辅助工具,用于在程序运行时检查一个布尔表达式是否为真。如果表达式为假(即0或false),则assert
会输出一条错误消息(通常包含失败的表达式、文件名和行号),并调用abort
函数来终止程序。这有助于开发者快速定位并修复程序中的错误。
assert
宏定义在<cassert>
(或C风格的<assert.h>
)头文件中。使用assert
时,不需要包含任何错误处理代码,因为当断言失败时,程序会立即终止。然而,这意呀着assert
主要用于调试阶段,而不应该用于生产环境中的错误处理。
#include <cassert>
#include <iostream>
int main() {
int x = 5;
assert(x > 0); // 这是一个有效的断言,因为x确实大于0
// 假设这里有一些代码改变了x的值
x = -1;
// 下面的断言会失败,因为x不再大于0
assert(x > 0); // 如果x不大于0,程序将输出错误信息并终止
std::cout << "This line will not be executed if the assertion fails." << std::endl;
return 0;
}
调试与生产:由于assert
在断言失败时会终止程序,因此它不适合用于生产环境中的错误处理。在生产代码中,应该使用更合适的错误处理机制,如异常处理。
性能影响:在编译时,如果定义了NDEBUG
宏(通常在发布构建中定义),则assert
宏会被忽略,不会生成任何代码。这有助于避免在发布版本中引入不必要的性能开销。
自定义错误消息:assert
宏允许你提供一个可选的字符串作为错误消息,这有助于在断言失败时提供更多上下文信息。
assert(x > 0 && "x should be greater than 0");
替代方案:对于需要更精细控制错误处理的情况,可以考虑使用异常处理或其他错误处理机制。
总之,assert
是C++中一种有用的调试工具,但应该谨慎使用,并了解其在不同编译设置下的行为。
namespace xny
{
class string
{
// ......
};
};
class string
{
private:
size_t _size; // 字符串的当前长度(不包括终止符 '\0')
size_t _capacity; // 字符串缓冲区的容量
char* _str; // 指向字符串数据的指针
public:
static size_t npos;
};
size_t string::npos = -1;
string()
:_size(0)
, _capacity(0)
, _str(new char[1])
{
_str[0] = '\0';
}
string(const char* str = "")
:_size(strlen(str))
, _capacity(_size)
, _str(new char[_capacity + 1])
{
// strcpy(_str, str);
memcpy(_str, str, _size + 1); // 替换strcpy的原因请见“全部代码”的"void test_string6()"部分
}
:_size(strlen(str))
:使用成员初始化列表来初始化成员变量_size
。这里,_size
被设置为传入字符串str
的长度(通过strlen(str)
计算得到)。如果str
为空字符串(即默认情况),则strlen(str)
返回0,因此_size
也会被初始化为0。
,_capacity(_size)
:紧接着,成员变量_capacity
(表示字符串的容量)被初始化为与_size
相同的值。这意味着在这个实现中,字符串的初始容量等于其长度,没有预留额外的空间用于未来的增长。然而,这种设计在实际应用中可能不是最高效的,因为每次字符串增长时都可能需要重新分配内存。
,_str(new char[_capacity + 1])
:最后,为字符串数据本身分配内存。这里分配了_capacity + 1
个字符的空间,其中_capacity个字符用于存储字符串内容,额外的1个字符用于存储字符串的终结符\0
(C风格字符串的标准结束符)。
strcpy(_str, str);
:在构造函数体内,使用strcpy
函数将传入的字符串str
(或其默认值空字符串)复制到之前分配的内存中(即_str
指向的位置)。这里假设strcpy
是安全的,即_str
指向的内存足够大,可以存储str
及其终结符\0
。由于之前已经通过成员初始化列表确保了这一点,所以这里的使用是安全的。
_str
是否为空的条件,避免出现释放空内存的情况。delete[]
而不是delete
,前者是基于new调用的次数来调用多少次,而后者固定只调用一次。~string()
{
if (_str)
{
delete[] _str; // 释放之前为字符串数据分配的内存
_str = nullptr; // 将指针置为nullptr,避免野指针问题
_size = _capacity = 0; // 将_size和_capacity都置0,防止内存泄露
}
}
string(const string& s)
{
_str = new char[s._capacity + 1];
// strcpy(_str, s._str);
memcpy(_str, s._str, s._size + 1);
_size = s._size;
_capacity = s._capacity;
}
【步骤解释】:
s. _str
的数据给 _str
,这里设置比较字节数为 s._size + 1
的原因是包含’\0’。void swap(string& tmp)
{
std::swap(_str, tmp._str);
std::swap(_size, tmp._size);
std::swap(_capacity, tmp._capacity);
}
string(const string& s)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
string tmp(s._str);
swap(tmp); // this->swap(tmp);
}
【步骤解释】:
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=(string tmp)
{
// this->swap(tmp);
swap(tmp);
return *this;
}
【注意】:
std::swap(tmp, *this);
呢?
const char* c_str() const
{
return _str;
}
size_t size() const
{
return _size;
}
char& operator[](size_t pos)
{
// 检查pos的合法性,设置断言
assert(pos < _size);
return _str[pos];
}
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
void reserve(size_t n)
{
// 如果请求的容量大于当前容量,则进行扩容
if (n > _capacity)
{
// 为新字符串分配足够的空间(包括额外的字符用于字符串终结符 '\0')
char* tmp = new char[n + 1];
// 拷贝旧字符串到新分配的空间中
// 注意:这里假设 _str 已经是一个以 '\0' 结尾的字符串
// strcpy(tmp, _str);
memcpy(tmp, _str, _size + 1);
// 释放旧的空间
delete[] _str;
// 更新指针和容量
_str = tmp;
_capacity = n;
// 注意:这里没有更新 _size,因为 reserve 只改变容量,不改变字符串的实际长度
// 如果需要,可以在扩容后根据需要更新 _size(例如,如果字符串是通过某种方式动态增长的)
}
// 如果 n <= _capacity,则不执行任何操作
}
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';
}
}
void append(const char* str)
{
size_t len = strlen(str);
if (_size +len > _capacity)
{
// 至少扩容到_size +len
reserve(_size + len);
}
// 将要追加的字符串复制到当前字符串的末尾
// strcpy(_str + _size, str);
memcpy(_str + _size, str, len + 1);
// 更新字符串的大小
_size += len;
// 注意:不需要再次设置终结符,因为 strcpy 已经做了这件事
}
+=
操作符重载// += (字符串)
string& operator+=(const char* str)
{
append(str);
return *this;
}
// += (单个字符)
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
// 中间插入单个字符
void insert(size_t pos, size_t n, char ch)
{
// 检查pos的合法性,设置断言
assert(pos <= _size);
if (_size + n > _capacity)
{
// 至少扩容n个单位
reserve(_size + n);
}
// 挪动数据方法1
/*int end = _size;
while (end >= (int)pos) // 将pos强转成int
{
_str[end + n] = _str[end];
--end;
}*/
// 挪动数据方法2
size_t end = _size;
while (end >= pos && end != npos) // 当end已经超过了目标位置并且不能很大的时候继续循环
{
_str[end + n] = _str[end];
--end;
}
// 覆盖原数据
for (size_t i = 0; i < n; i++)
{
_str[pos + i] = ch;
}
_size += n; // 更新字符串大小
}
// 中间插入一个字符串
void insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (_size + len > _capacity)
{
// 至少扩容到_size +len
reserve(_size + len);
}
// 挪动数据
size_t end = _size;
while (end >= pos && end != npos) // 当end已经超过了目标位置并且不能很大的时候继续循环
{
_str[end + len] = _str[end];
--end;
}
// 覆盖原数据
for (size_t i = 0; i < len; i++)
{
_str[pos + i] = str[i];
}
_size += len;
}
void erase(size_t pos, size_t len = npos)
{
// 检查pos的合法性,设置断言
assert(pos <= _size);
// 如果 len 是 npos 或 pos + len 超出当前大小,则删除从 pos 到末尾的所有内容
if (len == npos || pos + len >= _size)
{
_str[pos] = '\0'; // 直接使用终结符截断
_size = pos;
}
else
{
size_t end = pos + len;
while (end <= _size)
{
// 向前覆盖
_str[pos++] = _str[end++];
}
_size -= len; // 更新字符串大小
}
}
// 查找一个字符
size_t find(char ch, size_t pos = 0)
{
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
// 查找一个字符串
size_t find(const char* str, size_t pos = 0)
{
// 检查pos合法性,设置断言
assert(pos < _size);
const char* ptr = strstr(_str + pos, str); // 这里,strstr函数用于在_str字符串中从pos位置开始搜索子字符串str。
if (ptr)
{
// 返回子字符串在原字符串中的起始索引
return ptr - _str;
}
else
{
return npos;
}
}
void clear()
{
_str[0] = '\0';
_size = 0;
}
bool operator<(const string& s) const
{
size_t i1 = 0; // 左操作符
size_t i2 = 0; // 右操作符
while (i1 < _size && i2 < s._size)
{
if (_str[i1] < s._str[i2])
{
return true;
}
else if (_str[i1] > s._str[i2])
{
return false;
}
else
{
++i1;
++i2;
}
}
return _size < s._size;
}
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;
}
bool operator==(const string& s) const
{
return _size == s._size && memcmp(_str, s._str,_size) == 0;
}
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);
}
ostream& operator<<(ostream& out, const xny::string& s)
{
for (size_t i = 0; i < s.size(); i++)
{
out << s[i];
}
return out;
}
istream& operator>>(istream& in, xny::string& s)
{
char ch;
in >> ch;
while (ch != ' '&& ch != '\n')
{
s += ch;
in >> ch;
}
return in;
}
istream& operator>>(istream& in, xny::string& s)
{
s.clear();
char ch = in.get();
while (ch != ' ' && ch != '\n')
{
s += ch; // 如果循环+=的话,每一次都会扩容,当传入的数据很多的时候,扩容很多次会不好
ch = in.get();
}
return in;
}
istream& operator>>(istream& in, xny::string& s)
{
// 在读取新数据之前,先清空 s 中的现有内容。
s.clear();
char ch = in.get();
// 处理掉缓冲区前面的空格或者换行
while (ch == ' ' || ch == '\n')
{
// 不进行操作,直接取下一个字符就相当于清理
ch = in.get();
}
// 定义了一个字符数组 buff[128] 作为临时缓冲区,用于存储从输入流中读取的字符,避免出现上述的多次扩容的情况。
char buff[128];
int i = 0;
while (ch != ' ' && ch != '\n')
{
// 将读取的字符存储到 buff 中,并更新索引 i。
buff[i++] = ch;
// 如果 i 达到了 127(即缓冲区即将满),则将缓冲区的内容(加上字符串终止符 '\0')添加到 s 中,并重置 i 为 0,以便继续填充缓冲区。
if (i == 127) // 第128位是'\0'
{
buff[i] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
// 退出循环后,如果 i 不为 0,说明还有字符没有添加到 s 中,因此将 buff 中的剩余字符(加上字符串终止符 '\0')添加到 s 中。
if (i != 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
#pragma once
#include <cassert>
// *******提示:以下函数中基本都会实现普通对象的调用和常对象的调用
// 设置命名空间,防止与std命名空间冲突
namespace xny
{
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()
:_size(0)
, _capacity(0)
, _str(new char[1])
{
_str[0] = '\0';
}*/
// 构造函数
string(const char* str = "")
:_size(strlen(str))
, _capacity(_size)
, _str(new char[_capacity + 1])
{
// strcpy(_str, str);
memcpy(_str, str, _size + 1);
}
// 拷贝构造(深拷贝)
// 即拷贝时不只是简单的传值拷贝,这里还生成了新的空间用来存储拷贝后的数据
string(const string& s)
{
_str = new char[s._capacity + 1];
// strcpy(_str, s._str);
memcpy(_str, s._str, s._size + 1);
_size = s._size;
_capacity = s._capacity;
}
// 拷贝构造(优化)
/*string(const string& s)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
string tmp(s._str);
swap(tmp);
}*/
// 赋值运算符重载
// 传统写法
/*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);
// std::swap(_str, tmp._str);
// std::swap(_size, tmp._size);
// std::swap(_capacity, tmp._capacity);
// // std::swap(tmp, *this);
// // 这种写法是错误的,它会导致无穷递归,因为swap函数内部也在进行赋值运算,每次赋值都会调用swap函数,而每次swap也都会进行赋值
// }
// return *this;
//}
// 现代写法(优化)
void swap(string& tmp)
{
std::swap(_str, tmp._str);
std::swap(_size, tmp._size);
std::swap(_capacity, tmp._capacity);
}
string& operator=(string tmp)
{
// this->swap(tmp);
swap(tmp);
return *this;
}
// 析构函数
~string()
{
if (_str)
{
delete[] _str; // 释放之前为字符串数据分配的内存
_str = nullptr; // 将指针置为nullptr,避免野指针问题
_size = _capacity = 0; // 将_size和_capacity都置0,防止内存泄露
}
}
// 这里返回的是C风格的字符串
const char* c_str() const
{
return _str;
}
// 返回字符串的大小
size_t size() const
{
return _size;
}
// 重载下标索引[],在string中的索引比迭代器更加方便
char& operator[](size_t pos)
{
// 检查pos的合法性,设置断言
assert(pos < _size);
return _str[pos];
}
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
// 扩大字符串容量
void reserve(size_t n)
{
// 如果请求的容量大于当前容量,则进行扩容
if (n > _capacity)
{
// 为新字符串分配足够的空间(包括额外的字符用于字符串终结符 '\0')
char* tmp = new char[n + 1];
// 拷贝旧字符串到新分配的空间中
// 注意:这里假设 _str 已经是一个以 '\0' 结尾的字符串
// strcpy(tmp, _str);
memcpy(tmp, _str, _size + 1);
// 释放旧的空间
delete[] _str;
// 更新指针和容量
_str = tmp;
_capacity = n;
// 注意:这里没有更新 _size,因为 reserve 只改变容量,不改变字符串的实际长度
// 如果需要,可以在扩容后根据需要更新 _size(例如,如果字符串是通过某种方式动态增长的)
}
// 如果 n <= _capacity,则不执行任何操作
}
// 改变字符串的大小,使用特定字符填充
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';
}
}
// 尾插一个字符
void push_back(char ch)
{
if (_size == _capacity)
{
// 如果当前大小等于容量,则扩容
// 对于初始容量为0的情况,初始化为4;否则,使用二倍扩容策略
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
// 在字符串末尾添加新字符
_str[_size] = ch;
// 更新字符串大小
++_size;
// 在新字符之后设置终结符
_str[_size] = '\0';
// 注意:由于 _size 已经在添加字符后增加,所以上面的 _str[_size] = '\0'
}
// 尾插一个字符串
void append(const char* str)
{
size_t len = strlen(str);
if (_size +len > _capacity)
{
// 至少扩容到_size +len
reserve(_size + len);
}
// 将要追加的字符串复制到当前字符串的末尾
// strcpy(_str + _size, str);
memcpy(_str + _size, str, len + 1);
// 更新字符串的大小
_size += len;
// 注意:不需要再次设置终结符,因为 strcpy 已经做了这件事
}
// += 操作符重载(字符串)
string& operator+=(const char* str)
{
append(str);
return *this;
}
// += 操作符重载(单个字符)
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
// 中间插入单个字符
void insert(size_t pos, size_t n, char ch)
{
// 检查pos的合法性,设置断言
assert(pos <= _size);
if (_size + n > _capacity)
{
// 至少扩容n个单位
reserve(_size + n);
}
// 挪动数据方法1
/*int end = _size;
while (end >= (int)pos) // 将pos强转成int
{
_str[end + n] = _str[end];
--end;
}*/
// 挪动数据方法2
size_t end = _size;
while (end >= pos && end != npos) // 当end已经超过了目标位置并且不能很大的时候继续循环
{
_str[end + n] = _str[end];
--end;
}
// 覆盖原数据
for (size_t i = 0; i < n; i++)
{
_str[pos + i] = ch;
}
_size += n; // 更新字符串大小
}
// 中间插入一个字符串
void insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (_size + len > _capacity)
{
// 至少扩容到_size +len
reserve(_size + len);
}
// 挪动数据
size_t end = _size;
while (end >= pos && end != npos) // 当end已经超过了目标位置并且不能很大的时候继续循环
{
_str[end + len] = _str[end];
--end;
}
// 覆盖原数据
for (size_t i = 0; i < len; i++)
{
_str[pos + i] = str[i];
}
_size += len;
}
void erase(size_t pos, size_t len = npos)
{
// 检查pos的合法性,设置断言
assert(pos <= _size);
// 如果 len 是 npos 或 pos + len 超出当前大小,则删除从 pos 到末尾的所有内容
if (len == npos || pos + len >= _size)
{
_str[pos] = '\0'; // 直接使用终结符截断
_size = pos;
}
else
{
size_t end = pos + len;
while (end <= _size)
{
// 向前覆盖
_str[pos++] = _str[end++];
}
_size -= len; // 更新字符串大小
}
}
// 查找一个字符
size_t find(char ch, size_t pos = 0)
{
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
// 查找一个字符串
size_t find(const char* str, size_t pos = 0)
{
// 检查pos合法性,设置断言
assert(pos < _size);
const char* ptr = strstr(_str + pos, str); // 这里,strstr函数用于在_str字符串中从pos位置开始搜索子字符串str。
if (ptr)
{
// 返回子字符串在原字符串中的起始索引
return ptr - _str;
}
else
{
return npos;
}
}
// 返回子字符串
string substr(size_t pos = 0, size_t len = npos)
{
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;
}
// 清空字符串
void clear()
{
_str[0] = '\0';
_size = 0;
}
// 重载'<',其余操作符可以复用
//bool operator<(const string& s) const
//{
// size_t i1 = 0; // 左操作符
// size_t i2 = 0; // 右操作符
// while (i1 < _size && i2 < s._size)
// {
// if (_str[i1] < s._str[i2])
// {
// return true;
// }
// else if (_str[i1] > s._str[i2])
// {
// return false;
// }
// else
// {
// ++i1;
// ++i2;
// }
// }
// return _size < s._size;
//}
// 优化后的比较运算符重载及复用
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;
}
bool operator==(const string& s) const
{
return _size == s._size && memcmp(_str, s._str,_size) == 0;
}
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);
}
private:
size_t _size; // 字符串的当前长度(不包括终止符 '\0')
size_t _capacity; // 字符串缓冲区的容量
char* _str; // 指向字符串数据的指针
public:
static size_t npos;
};
size_t string::npos = -1;
};
// 流插入重载
ostream& operator<<(ostream& out, const xny::string& s)
{
for (size_t i = 0; i < s.size(); i++)
{
out << s[i];
}
return out;
}
// 流提取重载1(错误版)
// 当读取到空格或换行符时,这些字符实际上没有被从输入流中“消耗”掉,这意味着如果你紧接着使用另一个 >> 操作符(比如再次读取一个xny::string),它将从空格或换行符开始读取,这就导致了死循环。
//istream& operator>>(istream& in, xny::string& s)
//{
// char ch;
// in >> ch;
// while (ch != ' '&& ch != '\n')
// {
// s += ch;
// in >> ch;
// }
//
// return in;
//}
// 流提取重载2(简易版)
//istream& operator>>(istream& in, xny::string& s)
//{
// s.clear();
//
// char ch = in.get();
// while (ch != ' ' && ch != '\n')
// {
// s += ch; // 如果循环+=的话,每一次都会扩容,当传入的数据很多的时候,扩容很多次会不好
// ch = in.get();
// }
//
// return in;
//}
// 流提取重载3 (优化版)
istream& operator>>(istream& in, xny::string& s)
{
// 在读取新数据之前,先清空 s 中的现有内容。
s.clear();
char ch = in.get();
// 处理掉缓冲区前面的空格或者换行
while (ch == ' ' || ch == '\n')
{
// 不进行操作,直接取下一个字符就相当于清理
ch = in.get();
}
// 定义了一个字符数组 buff[128] 作为临时缓冲区,用于存储从输入流中读取的字符,避免出现上述的多次扩容的情况。
char buff[128];
int i = 0;
while (ch != ' ' && ch != '\n')
{
// 将读取的字符存储到 buff 中,并更新索引 i。
buff[i++] = ch;
// 如果 i 达到了 127(即缓冲区即将满),则将缓冲区的内容(加上字符串终止符 '\0')添加到 s 中,并重置 i 为 0,以便继续填充缓冲区。
if (i == 127) // 第128位是'\0'
{
buff[i] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
// 退出循环后,如果 i 不为 0,说明还有字符没有添加到 s 中,因此将 buff 中的剩余字符(加上字符串终止符 '\0')添加到 s 中。
if (i != 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
using namespace std;
#include "string.h"
// 测验命名空间namespace和迭代器iterator
void test_string1()
{
xny::string s1("hello world");
cout << s1.c_str() << endl;
xny::string s2;
cout << s2.c_str() << endl;
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;
const xny::string s3("hello world");
s3[0];
// xny::string::const_iterator cit = s3.begin();
auto cit = s3.begin();
while (cit != s3.end())
{
// *cit += 1;
cout << *cit << " ";
++cit;
}
cout << endl;
xny::string::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it << " ";
++it;
}
cout << endl;
for (auto ch : s1)
{
cout << ch << " ";
}
cout << endl;
}
// 测验push_back()和'+='重载函数
void test_string2()
{
xny::string s1("hello world");
cout << s1.c_str() << endl;
s1.push_back(' ');
s1.push_back('#');
s1.append("hello xny");
cout << s1.c_str() << endl;
xny::string s2("hello world");
cout << s2.c_str() << endl;
s2 += ' ';
s2 += '#';
s2 += "hello xny";
cout << s2.c_str() << endl;
}
// 测验insert()
void test_string3()
{
xny::string s1("helloworld");
cout << s1.c_str() << endl;
s1.insert(5, 3, 'x');
cout << s1.c_str() << endl;
s1.insert(0, 3, 'x');
cout << s1.c_str() << endl;
xny::string s2("helloworld");
s2.insert(5, "*****");
cout << s2.c_str() << endl;
}
// 测验erase()
void test_string4()
{
xny::string s1("helloworld");
cout << s1.c_str() << endl;
s1.erase(5, 3);
cout << s1.c_str() << endl;
s1.erase(5, 30);
cout << s1.c_str() << endl;
}
// 测试find()和substr()
void test_string5()
{
xny::string url = "http://legacy.cpluscplus.com/reference/string/string/";
// 协议 域名 资源名
size_t pos1 = url.find("://");
xny::string protocol; // 协议
if (pos1 != xny::string::npos) // 用来判断是否找到了指定的字符串
{
protocol = url.substr(0, pos1);
}
cout << protocol << endl;
xny::string domain; // 域名
xny::string uri; // 资源
size_t pos2 = url.find('/', pos1 + 3); // 不能从起始位置开始找,会找到://
if (pos2 != xny::string::npos)
{
domain = url.substr(pos1 + 3, pos2 - (pos1 + 3));
uri = url.substr(pos2 + 1);
}
cout << domain << endl;
cout << uri << endl;
}
// 测试resize(),判断c_str()和重载 << 的区别
void test_string6()
{
xny::string s("hello world");
s.resize(8);
cout << s.c_str() << endl;
s.resize(13, 'x');
cout << s.c_str() << endl;
s.resize(20, 'y');
cout << s.c_str() << endl;
// c_str和重载后的<<打印区别
// c_str遇到'\0'就会终止,而<<是按size打印的
// 而strcpy也是会遇到'\0'就终止,于是为了防止出现问题,就将所有的strcpy全部换成memcpy
xny::string ss("hello world");
ss += '\0';
ss += "!!!!!!!!";
cout << ss.c_str() << endl;
cout << ss << endl;
}
// 测试流插入 >>
void test_string7()
{
xny::string s;
cin >> s;
cout << s << endl;
}
// 测试比较运算符 < , > , ==
void test_string8()
{
xny::string s1("hello");
xny::string s2("hello");
cout << (s1 < s2) << endl;
cout << (s1 > s2) << endl;
cout << (s1 == s2) << endl << endl;
xny::string s3("hello");
xny::string s4("helloxxx");
cout << (s3 < s4) << endl;
cout << (s3 > s4) << endl;
cout << (s3 == s4) << endl << endl;
xny::string s5("helloxxx");
xny::string s6("hello");
cout << (s5 < s6) << endl;
cout << (s5 > s6) << endl;
cout << (s5 == s6) << endl << endl;
}
// 测试拷贝构造和重载的赋值运算符 =
void test_string9()
{
xny::string s1("hello");
xny::string s2(s1);
cout << s1 << endl;
cout << s2 << endl;
xny::string s3("xxxxxxxxxxxxxx");
s1 = s3;
cout << s1 << endl;
cout << s3 << endl;
// 用来检测拷贝构造的优化写法的缺失
s3 += '\0';
s3 += "yyyyyyyyyyyyyyy";
cout << s3 << endl;
xny::string s4(s3);
cout << s4 << endl;
// 结果发现s4没有拷贝s3'\0'之后的部分
}
int main()
{
test_string9();
return 0;
}