1:实现一个字符串类。 简单汇总
最简单的方案,使用一个字符串指针,以及实际字符串长度即可。
参考stl的实现,为了提升string的性能,实际上单纯的字符串指针和实际长度是不够了,如上,有优化方案,除此之外,考虑reserve() 以及重置容量,迭代器的相关实现逻辑 (小字符串的存储优化,结合占用内存大小(4的倍数)分配适当的缓冲区更好)。
类中如果没有成员变量,类的大小为1,如果有成员变量,类的大小即为成员变量的大小。(以前研究过 sizeof(string)的长度 可能是28 或者32)
2:实现注意细节
1:c字符串指针+ 实际字符串的长度进行数据存储
2:内存的申请和释放 用new/delete 或者malloc/free都可以吧, 字符串的赋值借用strcpy 或者memcpy都可以吧。
3:默认构造函数 c字符串传参构造 拷贝构造(深拷贝) 移动构造(浅拷贝 std::move) 赋值复制构造 赋值移动构造(右参std::move)
4:注意整个过程中 const参数的修饰,以及相关函数返回值 返回引用对象和返回对象。
5:重载必要的运算符(+ = == ),输入输出的重载(<< >>),注意重载<<和>>时和friend关键字配合,以及实现细节。
6:c字符串相关函数(strcpy strcmp strlen strcat strchr)
3:源码测试
3.1 类的声明(思考const,noexcept修饰,复制传参,引用传参,以及返回值返回引用 返回对象)
/*************************************
1:不需要考虑太多,直接字符串指针和长度进行实现。
2:考虑该类的大小,如果字符串为null时,如果字符串有长度时。 (字符串拼接 截断等)
3:字符串性能的优化,短字符串直接存栈中,长的进行申请。 其他考虑迭代器等的实现方案。
只考虑指针和长度进行实现:
1:默认构造函数。
2:C字符串初始化,直接初始化,移动构造。 拷贝构造函数,赋值构造函数。
3:重载运算符 = == + << >> +=
**************************************/
#include <iostream>
#include <cstring>
#include <stdio.h>
class my_string
{
private:
char* data;
size_t length;
public:
//默认构造函数 c字符串构造 拷贝构造函数 移动构造函数 赋值构造函数
my_string();
my_string(const char* str);
my_string(const my_string& other);
my_string(my_string&& other) noexcept;
~my_string();
size_t size() const;
const char* c_str() const;
//= == + <<
my_string& operator = (const my_string& other) noexcept; //赋值构造函数 复制
my_string& operator = (my_string&& other) noexcept; //移动赋值构造函数
bool operator==(const my_string& other) const;
my_string operator +(const my_string& other) const;
//注意friend的定义
friend std::ostream& operator<<(std::ostream& os, const my_string& str);
friend std::istream& operator>>(std::istream& in, my_string& str); //输入需要可修改
};
3.2 类的定义
//函数前面用const 表示的是函数返回值const
//函数后面用用const修饰 可以使常量调用,表示该函数内部不可以修改成员变量
my_string::my_string() :data(nullptr), length(0)
{
printf("my_string() \n");
data = new char[1];
data[0] = '\0';
}
//c字符串对string进行初始化 c字符串为NULL 不为NULL
//以及c字符串本身也是一以\0终止
my_string::my_string(const char* str)
{
printf(" my_string(const char * str) = %s \n", str);
if (str)
{
length = strlen(str); //str为NULL 会导致抛异常
data = new char[length + 1];
strcpy(data, str);
}
else
{
data = new char[1];
data[0] = '\0';
length = 0;
}
}
//字符串之间 拷贝构造函数 使用传递引用的方式提升性能
//在自己类的成员函数中 参数可以直接访问私有成员
my_string::my_string(const my_string& other)
{
printf(" my_string(const my_string & str) = %s \n", other.data);
length = other.length;
data = new char[length + 1];
strcpy(data, other.data);
}
//移动构造函数 注意&& 浅拷贝
my_string::my_string(my_string&& other) noexcept
{
printf(" my_string(my_string && str) = %s \n", other.data);
//不用申请内存 直接进行赋值即可
data = other.data;
length = other.length;
other.data = nullptr;
other.length = 0;
}
my_string::~my_string()
{
printf("~my_string() = %s \n", data);
if(data)
delete[]data;
}
size_t my_string::size() const
{
return length;
}
const char* my_string::c_str() const
{
return data;
}
//= == + <<
//深拷贝 注意对象可能同一个 返回对象的引用
my_string& my_string::operator = (const my_string& other) noexcept//赋值构造函数 复制
{
printf("operator = (const my_string & other) = %s \n", other.data);
if (this == &other)
{
return *this;
}
//赋值 先清理 再赋值
delete[]data;
length = other.length;
data = new char[length + 1];
strcpy(data, other.data);
return *this;
}
//浅拷贝 注意对象可能一个 返回引用
my_string& my_string::operator = (my_string&& other) noexcept //移动赋值构造函数
{
printf("operator = (my_string && other) = %s \n", other.data);
if (this == &other)
{
return *this;
}
data = other.data;
length = other.length;
other.data = nullptr;
other.length = 0;
return *this; //返回对象的引用
}
//判断两个字符串相等
bool my_string::operator==(const my_string& other) const
{
printf("operator==(const my_string &other) = %s \n", other.data);
return strcmp(data, other.data) == 0;
}
my_string my_string::operator +(const my_string& other) const
{
printf("operator +(const my_string & other) = %s \n", other.data);
my_string NewString;
NewString.length = length + other.length;
NewString.data = new char[NewString.length + 1];
strcpy(NewString.data, data);
strcpy(NewString.data, other.data);
return NewString; //函数中定义的对象 返回该拷贝
}
注意operator<< 和operator>>和friend的细节。
//输出
std::ostream& operator <<(std::ostream& os, const my_string& str)
{
os << str.data;
return os;
}
std::istream& operator>>(std::istream& in, my_string& str)
{
//或者按行输入获取字符串
const size_t BUFFER_SIZE = 1024;
char buffer[BUFFER_SIZE] = { 0 };
in >> buffer;
str.length = strlen(buffer);
delete[] str.data; //理论上都是已有对象进行输入
str.data = new char[str.length + 1];
strcpy(str.data, buffer);
return in;
}
3.3 测试代码
int main()
{
//默认构造函数测试
my_string str; //默认构造函数
my_string str_c("c string"); //c字符串构造
my_string str_str(str_c); //拷贝构造吧
printf("size = %d dara = %s \n", str_str.size(), str_str.c_str());
my_string str_move(std::move(str_str)); //移动语义
if (str_str.c_str() == nullptr)
{
printf("size = %d dara = nullptr \n", str_str.size());
}
printf("size = %d dara = %s \n", str_move.size(), str_move.c_str());
std::cout << "\nos =" << str_move << std::endl;
//直接初始化
my_string str_move1 = std::move(str_move);
std::cout << "\nstr_move1 =" << str_move1 << std::endl;
//std::cout << "\nstr_move =" << str_move << std::endl;
//赋值构造
my_string str_cp = str_move1;
std::cout << "\nstr_move1 =" << str_move1 << std::endl;
std::cout << "\nstr_cp =" << str_cp << std::endl;
//赋值初始化
my_string str_cp_by_operator;
my_string str_move_by_operator;
str_cp_by_operator = str_cp;
std::cout << "\nstr_cp_by_operator =" << str_cp_by_operator << std::endl;
std::cout << "str_cp =" << str_cp << std::endl;
str_move_by_operator = std::move(str_cp);
std::cout << "\nstr_move_by_operator =" << str_cp_by_operator << std::endl;
if (str_cp.c_str() != nullptr)
{
std::cout << "str_cp =" << str_cp << std::endl;
}
if (str_cp_by_operator == str_move_by_operator)
{
printf("\n operator == is success! \n");
}
else
{
printf("\n operator == is failed! \n");
}
my_string add = str_cp_by_operator + str_move_by_operator;
std::cout << "add =" << add << std::endl;
printf("please input str:==>");
std::cin >> str;
std::cout << "os =" << str << std::endl;
return 0;
}