目录
[2. 拷贝构造函数与"="](#2. 拷贝构造函数与“=”)
[4. 增删查改版STL-string](#4. 增删查改版STL-string)
1.构造函数&析构函数
想要实现下面的代码,我们该如何编写构造函数?
cpp
void test_string1()
{
string s1("hello");
string s2;
}
string本身其实只需要一个字符指针;构造函数使用全缺省的一个写法,我们可以这么写:
string.h
cpp
#pragma once
#include<iostream>
namespace luo_string
{
class string
{
private:
char* _str;
public:
// 全缺省构造函数
string(const char* str = "")
:_str(new char[strlen(str) + 1])
{
strcpy(_str, str);
}
~string()
{
delete[] _str;
_str = nullptr;
}
size_t size()
{
return strlen(_str);
}
char& operator[](size_t i) // 返回引用,能修改
{
return _str[i];
}
};
void test_string1()
{
string s1("hello");
string s2;
}
}
①首先为了避免命名冲突,我们可以写一个自己的命名空间,所有代码写在这个命名空间之内;
②接下来看构造函数,需要传入一个字符串,也就是const char* 类型的数据,所以需要用此类型作为形参,如果没有传入任何参数,这里就默认给"",也就是空字符串,这里的空字符串包含一个字符也就是\0,这里给_str分配空间使用strlen对传入的数据计算 + 1,最后使用strcpy拷贝数据;
③析构函数就非常简单,直接将_str的空间释放,_str给一个nullptr,避免野指针;
2. 拷贝构造函数与"="
如果不实现拷贝构造函数,那么下面的操作会调用系统默认的拷贝构造函数,那么就会出现问题;
cpp
void test_string2()
{
string s1("hello");
string s2(s1);
}
系统默认的构造函数是浅拷贝,也就是说,两个指针指向同一块内存地址;析构函数会调用两次;
具体流程:
s1构造------》s2构造
s2析构------》s1析构
s2析构的时候先将内存释放,再将s2置空;
s1此时仍然指向那个被释放的空间,就会再次对内存空间释放,就会产生报错;

cpp
// 拷贝构造函数
string(const string& s)
:_str(new char[strlen(s._str) + 1])
{
strcpy(_str,s._str);
}
那么赋值运算符"=",也有这样的特点;
这里重载一下赋值运算符就可以了;这里需要注意内存泄露的问题,例如s1 = s3,这里需要将s1之前的老空间进行释放;

需要给一个额外的空间,将数据传入之后,需要将this指向的老空间释放,再指向新空间;
如果出现自己 = 自己的情况,会将这个流程跳过,直接返回自己;
cpp
string& operator=(const string& s)
{
// 防止自己 = 自己
if (this != &s)
{
char* tmp = new char[strlen(s._str) + 1];
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;
}
return *this;
}
3.简易版STL-string代码
cpp
#pragma once
#include<iostream>
namespace luo_string
{
class string
{
private:
char* _str;
public:
// 有参和无参的构造函数
// 全缺省构造函数
string(const char* str = "")
:_str(new char[strlen(str) + 1])
{
strcpy(_str, str);
}
// 拷贝构造函数
string(const string& s)
:_str(new char[strlen(s._str) + 1])
{
strcpy(_str, s._str);
}
~string()
{
delete[] _str;
_str = nullptr;
}
size_t size()
{
return strlen(_str);
}
char& operator[](size_t i) // 返回引用,能修改
{
return _str[i];
}
string& operator=(const string& s)
{
// 防止自己 = 自己
if (this != &s)
{
char* tmp = new char[strlen(s._str) + 1];
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;
}
return *this;
}
};
void test_string1()
{
string s1("hello");
string s2;
}
void test_string2()
{
string s1("hello");
string s2(s1);
string s3 = s2;
}
}
4. 增删查改版STL-string
resize详解:
需求:需要将string设置成指定的size大小,使用指定的字符进行填充;
假设:此时的size = 5,capacity = 10;
情况一:将size设置成2,直接将2这个位置填充为'\0',size = 2即可;
情况二:将size设置成8,从size开始到8之前的位置全部替换为ch;,8的位置替换为'\0';
情况三:将size设置成18,先扩容,将capacity变成18,再进行替换操作,同情况二;
cpp
void resize(size_t n,char ch = '\0')
{
if (n < _size)
{
_str[n] = '\0';
_size = n;
}
else
{
if (n > _capacity)
{
reserve(n);
}
// 写数据
for (size_t i = _size; i < n; i++)
{
_str[i] = ch;
}
_size = n;
_str[_size] = '\0';
}
}
erase详解:
需求:将指定的位置删除指定的长度;
例如:helloword\0,删除pos=5位置,size = 2、5、8个长度;
情况一:从下标5,删除2个元素,其实就是下标为pos + len的元素往前覆盖到pos的位置,被覆盖的下标和覆盖的下标依次++;
情况二、三:删除的len + pos > _size,说明要全部删除完毕;如果不传默认参数为string::npos为整数最大值;此时只需要将pos置为'\0'即可,最后将size变为size - len即可;
cpp
void erase(size_t pos, size_t len = npos)
{
if (len + pos >= _size) // 说明此处到后面直接删完
{
_str[pos] = '\0';
_size = pos;
}
else
{
size_t i = pos + len;
// 此处的位置往前挪一位
for ( ; i <= _size; i++)
{
_str[i - len] = _str[i];
}
_size -= len;
}
};

完整代码&测试
cpp
#pragma once
#include<iostream>
#include<assert.h>
//namespace luo_string
//{
// class string
// {
// private:
// char* _str;
//
//
// public:
// // 全缺省构造函数
// string(const char* str = "")
// :_str(new char[strlen(str) + 1])
// {
// strcpy(_str, str);
// }
//
// // 拷贝构造函数
// string(const string& s)
// :_str(new char[strlen(s._str) + 1])
// {
// strcpy(_str, s._str);
// }
//
// ~string()
// {
// delete[] _str;
// _str = nullptr;
// }
// size_t size()
// {
// return strlen(_str);
// }
// char& operator[](size_t i) // 返回引用,能修改
// {
// return _str[i];
// }
//
// string& operator=(const string& s)
// {
// // 防止自己 = 自己
// if (this != &s)
// {
// char* tmp = new char[strlen(s._str) + 1];
// strcpy(tmp, s._str);
// delete[] _str;
// _str = tmp;
// }
// return *this;
// }
//
// };
namespace luo_string
{
typedef char* iterator;
class string
{
friend std::ostream& operator<<(std::ostream& out, const string& s);
friend std::istream& operator>>(std::istream& in, string& s);
private:
char* _str;
size_t _size;
size_t _capacity;
static size_t npos;
public:
// 全缺省构造函数
string(const char* str = "")
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// 拷贝构造函数
string(const string& s)
:_str(new char[strlen(s._str) + 1])
{
strcpy(_str, s._str);
_capacity = s._capacity;
_size = s._size;
}
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
size_t size()const
{
return _size;
}
size_t capacity()const
{
return _capacity;
}
char& operator[](size_t i) // 返回引用,能修改
{
assert(i < _size);
return _str[i];
}
const char& operator[](size_t i)const // const对象的重载
{
assert(i < _size);
return _str[i];
}
string& operator=(const string& s)
{
// 防止自己 = 自己
if (this != &s)
{
char* tmp = new char[strlen(s._str) + 1];
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
const char* c_str()
{
return _str;
}
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[]_str;
_str = tmp;
_capacity = n;
}
}
// 插入一个字符
void push_back(char ch)
{
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;
reserve(newcapacity);
}
_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;
}
// 重载+=
string& operator+=(const char ch)
{
this->push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
this->append(str);
return *this;
}
// 插入单个字符
void insert(size_t pos, char ch)
{
assert(pos < _size);
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;
reserve(newcapacity);
}
size_t end = _size;
// 从后往前挪
while (pos <= end)
{
_str[end + 1] = _str[end]; // '\0'放到后面,以此类推
--end;
}
_str[pos] = ch;
++_size;
}
// 插入多个字符
void insert(size_t pos, const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
size_t end = _size;
while (pos <= end)
{
// 每一个值[len]下一个值
_str[end + len] = _str[end];
--end;
}
// 赋值
/*for (size_t i = 0; i < len; i++)
{
_str[pos+i] = str[i];
}*/
// 这里不要使用strcpy会将\0一起拷贝
strncpy(_str + pos, str, len);
_size += len;
}
// 将size大小变为n
/*
_size = 5,_capacity = 10
情况1:变为8
情况2:变为18
情况3:变为2
*/
void resize(size_t n, char ch = '\0')
{
if (n < _size)
{
_str[n] = '\0';
_size = n;
}
else
{
if (n > _capacity)
{
reserve(n);
}
// 写数据
for (size_t i = _size; i < n; i++)
{
_str[i] = ch;
}
_size = n;
_str[_size] = '\0';
}
}
void erase(size_t pos, size_t len = npos)
{
if (len + pos >= _size) // 说明此处到后面直接删完
{
_str[pos] = '\0';
_size = pos;
}
else
{
size_t i = pos + len;
// 此处的位置往前挪一位
for (; i <= _size; i++)
{
_str[i - len] = _str[i];
}
_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 string::npos;
}
// 查找子串
size_t find(const char* str, size_t pos = 0)
{
// 直接调用c语言库,返回这个位置的指针
char* p = strstr(_str, str);
if (p == nullptr)
{
return string::npos;
}
else
{
return (p - _str);// 返回这个元素的下标
}
}
bool operator<(const string& s)
{
int ret = strcmp(this->_str, s._str);
return ret < 0;
}
bool operator==(const string& s)
{
int ret = strcmp(this->_str, s._str);
return ret == 0;
}
bool operator<=(const string& s)
{
return *this < s || *this == s;
}
bool operator>(const string& s)
{
return !(*this <= s);
}
bool operator>=(const string& s)
{
return *this > s || *this == s;
}
bool operator!=(const string& s)
{
return !(*this == s);
}
};
size_t string::npos = -1; // -1给size_t就是整数最大值
std::ostream& operator<<(std::ostream& out, const string& s)
{
out << s._str;
return out;
}
std::istream& operator>>(std::istream& in, string& s)
{
char ch;
//in >> ch; // 拿不到回车
while (1)
{
ch = in.get();
if (ch == ' ' || ch == '\n')
{
break;
}
else {
s += ch;
}
}
return in;
}
void test_string1()
{
string s1("hello");
string s2;
}
void test_string2()
{
string s1("hello");
string s2(s1);
string s3 = s2;
luo_string::iterator it = s1.begin();
while (it != s1.end())
{
std::cout << *it << " ";
++it;
}
// 本身没有对范围for做支持,编译器会把范围for直接转换为迭代器
for (auto e : s1)
{
std::cout << e;
}
s1.push_back('a');
s1.append("what the fuck\n");
// 本身没有对范围for做支持,编译器会把范围for直接转换为迭代器
s1 += "hello world\n";
s1 += 'y';
for (auto e : s1)
{
std::cout << e;
}
}
void test_string3()
{
string s1("hello");
s1.insert(2, "fuck");
for (auto e : s1)
{
cout << e;
}
}
void test_string4()
{
string s1 = "hello";
s1.reserve(10); // 容量变成10
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl << endl;
s1.resize(8, 'c');
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl << endl;
s1.resize(2, 'b');
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl << endl;
s1.resize(18, 'a');
cout << s1 << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
}
void test_string5()
{
string s1 = "abcdef";
cout << s1.find("bcd") << endl;
cout << s1.find("bed") << endl;
string s2;
cin >> s2;
cout << s2 << endl;
}
}