文章目录
- string初始工作(默认构造、析构、初始化……)
- 1.string篇
- 2.string修正篇
- 3.修正string 添加析构、声明定义分离
- 4.string修正后——模板
- string库功能实现,底层代码(非万能)
- 一、核心实现——operator[]重载、范围for(包括const修饰和非const修饰)
- 二、核心实现 string,reserve、push_back、append、+=、insert插入(单个字符)
- 核心实现insert(插入一段数据),erase删除,string拷贝构造,operator=运算符重载。
- 三、核心实现 深拷贝、substr、find、>> << 各种运算符重载、getline
- 补充——深拷贝实现、swap和strcpy的踩坑点
- 知识点总结
- 一、&既然修改原始值,那么是否还会调用默认构造?
- 二、构造函数就一个 但是却能string 也能const string?
- 三、.1_capcity、_size书写与扩容问题、具体如何区分什么时候该括_capacity\_size 以及扩多少?
- .2 new char[_capacity+1] 会不会扩容_capacity? strcpy(_str,str)之后会不会自动改变_size?
- 3. delete[]_str 之后能否进行 strcpy(_str,str)?
- 四、C语言的for循环在更新数据的弊端
string初始工作(默认构造、析构、初始化……)
全文侧重于,string底层实现原理、并结合class类知识点重合混杂之后可能出现的问题、以及修正方法。
1.string篇
string.h
#pragma once
//头文件的包含{string、iostream、strcpy}
#define _CRT_SECURE_NO_WARNINGS
#include<string>#include<iostream>using namespace std;namespace bit{//定义类stringclass string{public://构造函数——无参string():_str(nullptr), _size(0), _capacity(0){}//构造函数——有参string(const char* str):_str(new char[strlen(str) + 1]), _size(strlen(str)), _capacity(strlen(str))/*Q2::1.string3个初始化列表初始化 进行了三次strlen接口调用,极大浪费效率2.这里初始化列表的顺序和私有成员函数定义的顺序保持一致,错误的写法为string(const char* str):_size(strlen(str)),_str(new char[_size + 1]), _capacity(_size)修改:1.只将_size 走初始化列表,_str 和 _capacity 走内部函数默认初始化*/{}//构造c_strchar* c_str(){return _str;}//析构函数~string(){delete[]_str;_str = nullptr;_size = 0;_capacity = 0;}//private私有成员private:char* _str;size_t _size;size_t _capacity;};}
test.cpp
//头文件包含
#include"string.h"
//测试样例1
namespace bit
{
void test_string1()
{
string s1;
cout << s1.c_str() << endl;
/*Q1::
1.这里输出的s1.c_str程序会发生崩溃
c_str是C语言中的语法,其侧重的是传值返回,
而原始的_str内存空间为空,传值返回必然伴随解引用,因此会发生越界访问
修改:
1.将原始空间设置缺省值 空间指定为1 存入\0
*/
//string s2("hello world");
//cout << s2.c_str() << endl;
}
}
//main函数
int main()
{
bit::test_string1();
return 0;
};
string.cpp
//无
2.string修正篇
string.h
#pragma once
//头文件的包含{string、iostream、strcpy}
#define _CRT_SECURE_NO_WARNINGS
#include<string>#include<iostream>using namespace std;namespace bit{//定义类stringclass string{public://构造函数——无参string():_str(new char[1] {'\0'}), _size(0), _capacity(0){}//构造函数——有参string(const char* str): _size(strlen(str)){_str = new char[_size + 1];_capacity = _size;strcpy(_str, str);}//构造c_strchar* c_str(){return _str;}//析构函数~string(){delete[]_str;_str = nullptr;_size = 0;_capacity = 0;}//private私有成员private:char* _str;size_t _size;size_t _capacity;};}
test.cpp
//包含头文件
#include "string.h"
//定义命名空间
namespace bit
{
//特使样例1
void test_string1()
{
std::string s1;
cout << s1.c_str() << endl;
string s2("hello world");
cout << s2.c_str() << endl;
}
}
//mian
int main()
{
bit::test_string1();
return 0;
}
string.cpp
//无
3.修正string 添加析构、声明定义分离
string.h
#pragma once
//头文件的包含{string、iostream、strcpy}
#define _CRT_SECURE_NO_WARNINGS
#include<string>#include<iostream>using namespace std;namespace bit{//定义类stringclass string{public:/*//构造函数——无参string():_str(new char[1] {'\0'}), _size(0), _capacity(0){}*///Q1:修正一、//构造函数——有参、无参混合到一起//设置缺省值、若为空则使用缺省值、若不为空贼使用str传入的stringstring(const char* str = "");/* : _size(strlen(str)){_str = new char[_size + 1];_capacity = _size;strcpy(_str, str);}*///构造c_strchar* c_str();/*{return _str;}*///Q2:修正二、//析构函数~string();/*{delete[]_str;_str = nullptr;_size = 0;_capacity = 0;}*///private私有成员private:char* _str;size_t _size;size_t _capacity;};}
test.cpp
//头文件包含
#include"string.h"
//测试样例1
namespace bit
{
void test_string1()
{
string s1;
cout << s1.c_str() << endl;
string s2("hello world");
cout << s2.c_str() << endl;
}
}
//main函数
int main()
{
bit::test_string1();
return 0;
};
string.cpp
//Q3:修正三
//声明定义分离
#include"string.h"
namespace bit
{
string::string(const char* str) //声明定义分离不能同时设置缺省值
: _size(strlen(str))
{
_str = new char[_size + 1];
_capacity = _size;
strcpy(_str, str);
}
//构造c_str
char* string::c_str()
{
return _str;
}
//析构
string::~string()
{
delete[]_str;
_str = nullptr;
_size = 0;
_capacity = 0;
}
}
这里面有一个需要注意的知识点 就是声明和定义分离时不能同时设置缺省值
定义的时候都需要指名属于string::域 相对来说比较麻烦
4.string修正后——模板
后续类功能都是在此模板上添加,修改
string.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS //strcpy
#include<string>#include<iostream>using namespace std;namespace bit{//定义类stringclass string{public://构造函数——有参、无参混合到一起string(const char* str = "");//设置缺省值、若为空则使用缺省值、若不为空贼使用str传入的string//构造c_strchar* c_str();//析构函数~string();//private私有成员private:char* _str;size_t _size;size_t _capacity;};}
test.cpp
//头文件包含
#include"string.h"
//测试样例1
namespace bit
{
void test_string1()
{
string s1;
cout << s1.c_str() << endl;
string s2("hello world");
cout << s2.c_str() << endl;
}
}
//main函数
int main()
{
bit::test_string1();
return 0;
};
string.cpp
//声明定义分离
#include"string.h"
namespace bit
{
//默认构造
string::string(const char* str) //声明定义分离不能同时设置缺省值
: _size(strlen(str))
{
_str = new char[_size + 1];
_capacity = _size;
strcpy(_str, str);
}
//构造c_str
char* string::c_str()
{
return _str;
}
//析构
string::~string()
{
delete[]_str;_str = nullptr;_size = 0;_capacity = 0;
}
}
string库功能实现,底层代码(非万能)
一、核心实现——operator[]重载、范围for(包括const修饰和非const修饰)
string.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS //strcpy
#include<string>#include<iostream>#include<assert.h>using namespace std;namespace bit{//定义类stringclass string{public://构造函数——有参、无参混合到一起string(const char* str = "");//设置缺省值、若为空则使用缺省值、若不为空贼使用str传入的string//构造c_strchar* c_str()const;//析构函数~string();//operator[]的实现 char& operator[](int pos)const;//实现autotypedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}//实现const autotypedef const char* const_iterator;const_iterator begin()const{return _str;}const_iterator end()const{return _str + _size;}//private私有成员private:char* _str; size_t _size; size_t _capacity;};}
string.cpp
//声明定义分离
#include"string.h"
namespace bit
{
//默认构造
string::string(const char* str) //声明定义分离不能同时设置缺省值
: _size(strlen(str))
{
_str = new char[_size + 1];
_capacity = _size;
strcpy(_str, str);
}
//构造c_str
char* string::c_str()const
{
return _str;
}
//operator[]
char& string::operator[](int pos)const
{
assert(pos < _size);
return _str[pos];
}
//析构
string::~string()
{
delete[]_str; _str = nullptr; _size = 0; _capacity = 0;
}
}
test.cpp
//头文件包含
#include"string.h"
//测试样例1
namespace bit
{
void test_string1()
{
string s1;
cout << s1.c_str() << endl;
string s2("hello world");
cout << s2.c_str() << endl;
s2[0] = 'x';
cout << s2.c_str() << endl;
//范围for实现
string s3("My name is ljy.");
for (auto ch:s3)
{
cout << ch << "-";
}
cout << endl;
const string s4("What's your name?");
for (auto c_ch : s4)
{
cout << c_ch << "-";
}
cout << endl;
}
}
//main函数
int main()
{
bit::test_string1();
return 0;
};
二、核心实现 string,reserve、push_back、append、+=、insert插入(单个字符)
string.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS //strcpy
#include<string>#include<iostream>#include<assert.h>#include<algorithm>using namespace std;namespace bit{//定义类stringclass string{public://构造函数——有参、无参混合到一起string(const char* str = "");//设置缺省值、若为空则使用缺省值、若不为空贼使用str传入的string//构造c_strchar* c_str()const;//析构函数~string();//operator[]的实现 char& operator[](int pos)const;//实现autotypedef char* iterator;iterator begin();iterator end();//实现const autotypedef const char* const_iterator;const_iterator begin()const;const_iterator end()const;//push_back 的实现void push_back(char ch);//reserve 的实现void reserve(size_t n);//append的实现void append(const char* str);//+=字符串的实现string& operator+=(const char* str);//+=单个字符的实现string& operator+=(char ch);//private私有成员//insert函数void insert(size_t pos, char ch);private:char* _str; size_t _size; size_t _capacity;//这里少一个npos的定义};}
string.cpp
//声明定义分离
#include"string.h"
namespace bit
{
//默认构造
string::string(const char* str) //声明定义分离不能同时设置缺省值
: _size(strlen(str))
{
_str = new char[_size + 1];
_capacity = _size;
strcpy(_str, str);
}
//构造c_str
char* string::c_str()const
{
return _str;
}
//operator[]
char& string::operator[](int pos)const
{
assert(pos < _size);
return _str[pos];
}
//析构
string::~string()
{
delete[]_str; _str = nullptr; _size = 0; _capacity = 0;
}
//auto
string::iterator string::begin()
{
return _str;
}
string::iterator string::end()
{
return _str + _size;
}
//const auto
string::const_iterator string::begin()const
{
return _str;
}
string::const_iterator string::end()const
{
return _str + _size;
}
//reserve
void string::reserve(size_t n)
{
if (n > _capacity)
{
char* new_str = new char[n + 1];
strcpy(new_str, _str);
delete[]_str;
_str = new_str;
_capacity = n;
}
}
//push_back
void string::push_back(char ch)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_str[_size] = ch;
_size++;
_str[_size] = '\0';
}
//append
void string::append(const char* str)
{
int len = strlen(str);
int new_capacity = max(_size + len, _capacity * 2);
reserve(new_capacity);
strcpy(_str + _size, str);
_size = _size + len;
}
//+=字符串
string& string::operator+=(const char* str)
{
append(str);
return *this;
}
//+字符
string& string::operator+=(char ch)
{
push_back(ch);
return *this;
}
//insert
void string::insert(size_t pos, char ch)
{
assert(pos < _size);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
size_t end = _size;
while (end >= (int)pos)
{
_str[end + 1] = _str[end];
end--;
}
_str[pos] = ch;
}
}
test.cpp
//头文件包含
#include"string.h"
//测试样例1
namespace bit
{
void test_string1()
{
string s1;
cout << s1.c_str() << endl;
string s2("hello world");
cout << s2.c_str() << endl;
s2[0] = 'x';
cout << s2.c_str() << endl;
//范围for实现
string s3("My name is ljy.");
for (auto ch:s3)
{
cout << ch << "-";
}
cout << endl;
const string s4("What's your name?");
for (auto c_ch : s4)
{
cout << c_ch << "-";
}
cout << endl;
string s5("My name is ljy");
s5.push_back('.');
cout << s5.c_str() << endl;
s5.append(" What's your name?");
cout << s5.c_str() << endl;
string s6("hello");
s6 += " lijiaye";
s6 += '.';
cout << s6.c_str() << endl;
string s7("What' your name?");
s7.insert(5, 's');
cout << s7.c_str() << endl;
}
}
//main函数
int main()
{
bit::test_string1();
return 0;
};
核心实现insert(插入一段数据),erase删除,string拷贝构造,operator=运算符重载。
string.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS //strcpy
#include<string>#include<iostream>#include<assert.h>using namespace std;namespace bit{//定义类stringclass string{public://构造函数——有参、无参混合到一起string(const char* str = "");//设置缺省值、若为空则使用缺省值、若不为空贼使用str传入的string//构造c_strchar* c_str()const;//operator[] 访问char& operator[](size_t pos)const;//析构函数~string();//实现autotypedef char* iterator;typedef const char* const_iterator;iterator begin();iterator end();const_iterator begin()const;const_iterator end()const;//reserve实现void reserve(size_t n);//push_back 实现void push_back(char ch);//append的实现void append(const char* str);//insert的实现void insert(size_t pos, char ch);void insert(size_t pos, const char* s);//erase的实现void erase(size_t pos, size_t len);//拷贝构造的实现stringstring(const string& str);string& operator=(const string& str);//private私有成员private:char* _str;size_t _size;size_t _capacity;};}
string.cpp
//声明定义分离
#include"string.h"
namespace bit
{
//默认构造
string::string(const char* str) //声明定义分离不能同时设置缺省值
: _size(strlen(str))
{
_str = new char[_size + 1];
_capacity = _size;
strcpy(_str, str);
}
//构造c_str
char* string::c_str()const
{
return _str;
}
//operator[]
char& string::operator[](size_t pos)const
{
assert(pos < _size);
return _str[pos];
}
//析构
string::~string()
{
delete[]_str; _str = nullptr; _size = 0; _capacity = 0;
}
//auto 实现
string::iterator string::begin()
{
return _str;
}
string::iterator string::end()
{
return _str + _size;
}
string::const_iterator string::begin()const
{
return _str;
}
string::const_iterator string::end()const
{
return _str + _size;
}
void string::reserve(size_t n)
{
//单反设计空间的开辟,那么就必须引入新的指针,拷贝旧指针,清空旧的指针,旧指针指向新指针,更新容量
if (n > _capacity)
{
char* tmp = new char[n + 1];//CP2
strcpy(tmp, _str);
delete[]_str;
_str = tmp;
_capacity = n; //CP2
}
}
//push_back 实现
void string::push_back(char ch)
{
char* tmp = new char[_size + 2];
strcpy(tmp, _str);
delete[]_str;
_str = tmp;
_str[_size] = ch;
_size = _size + 1;
_str[_size] = '\0';
//CP1new char +2 的原因 和最后是垃圾值的原因
}
//append的实现
void string::append(const char* str)
{
int len = strlen(str);
reserve(max(_size + len, _capacity * 2));
//strcpy(_str + _size, str);
//_size = _size + len;
//Q3 为什么用for 就不行,而且为什么还需要后面加\0
//核心是 strcpy与\0有着密切关系
for (int i = 0; i < len; i++)
{
_str[_size] = str[i];
_size++;
}
_str[_size] = '\0';
}
//insert的实现
void string::insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
int end = _size;
while (end >= (int)pos)
{
_str[end + 1] = _str[end];
end--;
}
_str[pos] = ch;
}
//insert实现——传字符串
void string::insert(size_t pos, const char* s)
{
int len = strlen(s);
reserve(max(_size + len, _capacity * 2));
int end = _size;
while (end >= (int)pos)
{
_str[end + len] = _str[end];
end--;
}
strncpy(_str + pos, s, len); //Q25这里为什么写成_str[pos]就不对呢? 为什么要写成strncpy呢? strcpy为什么就不对呢?
}
//erase删除字符串
void string::erase(size_t pos, size_t len)
{
if (pos + len >= _size - 1)
{
_str[pos] = '\0';
_size = pos; //Q25调整空间大小
}
else
{
strcpy(_str + pos, _str + pos + len); //这里会把\0也拷贝过去
_size = _size - len;
}
}
//深拷贝构造
string::string(const string& str)
{
_str = new char[str._capacity + 1]; //开辟空间 开辟_capacity
strcpy(_str, str._str);
_size = str._size;
_capacity = str._capacity;
}
//运算符重载=
string& string::operator=(const string& str)
{
char* tmp = new char[str._capacity + 1];
strcpy(tmp, str._str);
delete[]_str;
_str = tmp; //Q25这里为什么不用拷贝构造?
_size = str._size;
_capacity = str._capacity;
return *this;
}
}
test.cpp
//头文件包含
#include"string.h"
//测试样例1
namespace bit
{
void test_string1()
{
string s1;
cout << s1.c_str() << endl;
string s2("hello world");
cout << s2.c_str() << endl;
s2[2] = 'x';
cout << s2.c_str()<<endl;
string s3("My name is ljy.");
for (auto ch : s3)
{
cout << ch;
}
cout << endl;
string s4("lj");
s4.push_back('y');
cout << s4.c_str() << endl;
string s5("My name is ljy.");
s5.append(" What's your name?");
cout << s5.c_str() << endl;
string s6("My ame is ljy.");
s6.insert(3, 'n');
cout << s6.c_str() << endl;
string s7("My name is");
s7.insert(10, " ljy.");
cout << s7.c_str() << endl;
string s8("Hello world");
s8.erase(6, 5);
cout << s8.c_str() << endl;
string s9("My namename is ljy.");
s9.erase(3, 4);
cout << s9.c_str() << endl;
}
}
//main函数
int main()
{
bit::test_string1();
return 0;
};
三、核心实现 深拷贝、substr、find、>> << 各种运算符重载、getline
string.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS //strcpy
#include<string>#include<iostream>#include<assert.h>using namespace std;namespace bit{//定义类stringclass string{public:friend std::ostream& operator<<(std::ostream& out, const string& str);friend istream& operator>>(istream& in, const string& str);friend istream& getline(istream& in, string& s, char delim);//构造函数——有参、无参混合到一起string(const char* str = "");//设置缺省值、若为空则使用缺省值、若不为空贼使用str传入的string//构造c_strchar* c_str()const;//operator[] 访问char& operator[](size_t pos)const;//析构函数~string();//实现autotypedef char* iterator;typedef const char* const_iterator;iterator begin();iterator end();const_iterator begin()const;const_iterator end()const;//reserve实现void reserve(size_t n);//push_back 实现void push_back(char ch);//+=字符串的实现string& operator+=(const char* str);//+=单个字符的实现string& operator+=(char ch);//append的实现void append(const char* str);//insert的实现void insert(size_t pos, char ch);void insert(size_t pos, const char* s);//erase的实现void erase(size_t pos, size_t len);//深拷贝构造的实现stringstring(const string& str);//operator重载=string& operator=(const string& str);//find-单个字符size_t find(char ch, size_t pos);//find—字符串size_t find(const char* str, size_t pos);//clearvoid clear(){_str[0] = '\0';_size = 0;}//substrstring substr(size_t pos, size_t len);//private私有成员private:char* _str;size_t _size;size_t _capacity;const static int npos = -1;};}//运算符<<重载ostream& operator<<(ostream& out, const string& str);//运算符>>重载istream& operator>>(istream& in, const string& str);//getlineistream& getline(istream& in, string& s, char delim);
string.cpp
//声明定义分离
#include"string.h"
namespace bit
{
//默认构造
string::string(const char* str) //声明定义分离不能同时设置缺省值
: _size(strlen(str))
{
_str = new char[_size + 1];
_capacity = _size;
strcpy(_str, str);
}
//构造c_str
char* string::c_str()const
{
return _str;
}
//operator[]
char& string::operator[](size_t pos)const
{
assert(pos < _size);
return _str[pos];
}
//析构
string::~string()
{
delete[]_str; _str = nullptr; _size = 0; _capacity = 0;
}
//auto 实现
string::iterator string::begin()
{
return _str;
}
string::iterator string::end()
{
return _str + _size;
}
string::const_iterator string::begin()const
{
return _str;
}
string::const_iterator string::end()const
{
return _str + _size;
}
void string::reserve(size_t n)
{
//单反设计空间的开辟,那么就必须引入新的指针,拷贝旧指针,清空旧的指针,旧指针指向新指针,更新容量
if (n > _capacity)
{
char* tmp = new char[n + 1];//CP2
strcpy(tmp, _str);
delete[]_str;
_str = tmp;
_capacity = n; //CP2
}
}
//push_back 实现
void string::push_back(char ch)
{
char* tmp = new char[_size + 2];
strcpy(tmp, _str);
delete[]_str;
_str = tmp;
_str[_size] = ch;
_size = _size + 1;
_str[_size] = '\0';
//CP1new char +2 的原因 和最后是垃圾值的原因
}
//+=字符串
string& string::operator+=(const char* str)
{
append(str);
return *this;
}
//+=字符
string& string::operator+=(char ch)
{
push_back(ch);
return *this;
}
//append的实现
void string::append(const char* str)
{
int len = strlen(str);
reserve(max(_size + len, _capacity * 2));
//strcpy(_str + _size, str);
//_size = _size + len;
//Q3 为什么用for 就不行,而且为什么还需要后面加\0
//核心是 strcpy与\0有着密切关系
for (int i = 0; i < len; i++)
{
_str[_size] = str[i];
_size++;
}
_str[_size] = '\0';
}
//insert的实现
void string::insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
int end = _size;
while (end >= (int)pos)
{
_str[end + 1] = _str[end];
end--;
}
_str[pos] = ch;
}
//insert实现——传字符串
void string::insert(size_t pos, const char* s)
{
int len = strlen(s);
reserve(max(_size + len, _capacity * 2));
int end = _size;
while (end >= (int)pos)
{
_str[end + len] = _str[end];
end--;
}
strncpy(_str + pos, s, len); //Q25这里为什么写成_str[pos]就不对呢? 为什么要写成strncpy呢? strcpy为什么就不对呢?
}
//erase删除字符串
void string::erase(size_t pos, size_t len)
{
if (pos + len >= _size - 1)
{
_str[pos] = '\0';
_size = pos; //Q25调整空间大小
}
else
{
strcpy(_str + pos, _str + pos + len); //这里会把\0也拷贝过去
_size = _size - len;
}
}
//深拷贝构造
string::string(const string& str)
{
_str = new char[str._capacity + 1]; //开辟空间 开辟_capacity
strcpy(_str, str._str);
_size = str._size;
_capacity = str._capacity;
}
//运算符重载=
string& string::operator=(const string& str)
{
char* tmp = new char[str._capacity + 1];
strcpy(tmp, str._str);
delete[]_str;
_str = tmp; //Q25这里为什么不用拷贝构造?
_size = str._size;
_capacity = str._capacity;
return *this;
}
//运算符重载<<
ostream& operator<<(ostream& out, const string& str)
{
for (auto ch : str)
{
cout << ch << "";
}
return out;
}
//运算符重载>>
istream& operator>>(istream& in, string& str) //Q9/28 >>ch 和scanf 和in.get的区别
{
str.clear();
char buff[256];
int i = 0;
char ch;
//in>>ch;
ch = in.get();
while (ch != '\n' && ch != ' ')
{
buff[i++] = ch;
if (i == 255)
{
buff[i] = '\0';
str += buff;
i = 0;
}
//str += ch;
//in>>ch;
ch = in.get();
}
if (i > 0)
{
buff[i] = '\0';
str += buff;
}
return in;
}
//find 查找单个字符
size_t string::find(char ch, size_t pos)
{
assert(pos < _size);
int i = pos;
for (i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
//find查找字符串
size_t string::find(const char* str, size_t pos)//Q9/28 char ch 、const char* s 、string&str
{
assert(pos < _size);
char* ptr = strstr(_str+pos, str);
if (ptr == nullptr)
{
return npos;
}
else
{
return ptr-_str;
}
}
//substr
string string::substr(size_t pos, size_t len)
{
assert(pos < _size);
if (len == npos || len > _size - pos)
{
len = _size - pos;
}
string sub;
sub.reserve(len);
for (size_t i= 0; i < len; i++)
{
sub += _str[pos + i];
}
return sub;
}
istream& getline(std::istream& in, string& s, char delim)
{
s.clear();
char buff[256];
int i = 0;
char ch;
//in >> ch;
ch = in.get();
while (ch != delim)
{
buff[i++] = ch;
if (i == 255)
{
buff[i] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
if (i > 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
//const size_t string::npos = -1;
}
}
test.cpp
//头文件包含
#include"string.h"
//测试样例1
namespace bit
{
void test_string1()
{
string s1;
cout << s1.c_str() << endl;
string s2("hello world");
cout << s2.c_str() << endl;
s2[2] = 'x';
cout << s2.c_str()<<endl;
string s3("My name is ljy.");
for (auto ch : s3)
{
cout << ch;
}
cout << endl;
string s4("lj");
s4.push_back('y');
cout << s4.c_str() << endl;
string s5("My name is ljy.");
s5.append(" What's your name?");
cout << s5.c_str() << endl;
string s6("My ame is ljy.");
s6.insert(3, 'n');
cout << s6.c_str() << endl;
string s7("My name is");
s7.insert(10, " ljy.");
cout << s7.c_str() << endl;
string s8("Hello world");
s8.erase(6, 5);
cout << s8.c_str() << endl;
string s9("My namename is ljy.");
s9.erase(3, 4);
cout << s9.c_str() << endl;
char ch = 'l';
string s10("My name is ljy.");
size_t insert1 = s10.find(ch, 5);
cout << insert1 << endl;
string s11("My name is ljy.");
size_t insert2 = s11.find("ljy", 5);
cout << insert2 << endl;
s11.clear();
cout << s11 << endl;
string s12("My name is ljy.");
cout << s12 << endl;
string s13;
s13 = s12.substr(3, 4);
cout << s13 << endl;
string s1, s2("xxxxxx");
cin >> s1 >> s2;
cout << s1 << endl;
cout << s2 << endl;
}
}
//main函数
int main()
{
bit::test_string1();
return 0;
};
补充——深拷贝实现、swap和strcpy的踩坑点
#include<iostream>#include<string>#include<assert.h>using namespace std;//命名空间namespace bit{class string{public://一、分析参数传入的是一个地址还是实参本身、这里是地址string(const char* str = ""){if (str == nullptr){assert(false);return;}//二、分析new char 需要的是一个具体的数值大小,这个大小需要strlen来实现,strlen传入的是地址而非实参,因此这里是strlen(str)_str = new char(strlen(str) + 1); //这里传入的是指针 ,而下面传入的是实参本身,因此两者不一样strcpy(_str, str);}//_str new char 空间//strcpy直接拷贝// :_size(strlen(str))//{// _str = new char[_size + 1];// _capacity = _size + 1;// strcpy(_str, str);//}string(const string& s) //传入的是s本身 而非指针,且不可变//初始化//三、分析分析参数传入的是一个地址还是实参本身、这里是实参本身,因此strlen要访问私有成员变量:_str(new char(strlen(s._str) + 1)) //要想再类里面使用strlen、_size _capacity注意成员变量是个很重要的角色//strcpy直接拷贝{strcpy(_str, s._str);}//{// char* tmp = new char[s._size + 1]; //这里不能加括号,加上括号就是函数// memcpy(tmp, s._str, _size + 1);// delete[]s;// _str = tmp;// _size = tmp._size;// _capacity = _size + 1;//}//四、分析重载运算符里面都有一个this指针,这里我们一定要传出this指针,运算符重载的共性string& operator=(const string& s){if (this != &s){delete[]_str;_str = new char(strlen(s._str) + 1);strcpy(_str, s._str);return *this;}}//这里默认有一个*this//先删除_str//_str new char新空间//strcpy拷贝//return *this~string(){delete[]_str;_str = nullptr;}//{// //delete[]// //_str指向新空间// delete[]_str;// _size = 0;// _capacity = 0;//}private:char* _str;};}int main(){return 0;}
通过swap实现
#include<string>#include<iostream>using namespace std;namespace ljy{class string{public:string(const char* str = "")//有参构造 无参构造: _str(new char(strlen(str) + 1)){strcpy(_str, str);}string(string& s){//char* tmp = new char(strlen(s._str) + 1);string tmp(s._str);swap(_str, tmp._str);}string& operator=(string& s){string tmp(s);swap(_str, tmp._str);}~string(){delete[]_str;_str = nullptr;}private:char* _str;};}int main(){return 0;}
详解,swap的好处
swap应注意的点
swap适用场景:1.swap适用于2个类之间的交换,(swap不仅交换值,还会交换成员地址)
2.swap适用于 2个已有成员,而非初始化(构造函数不适用swap的原因)
3.当我们交换完之后 还需要析构,还需要判断是否自引用 这时候用swap会非常非常方便。因为swap 不仅不会进行深拷贝那么复杂的运算,他还会调用完之后自动析构,(采用创建第三变量的方法),自引用的问题也可以很好的解决。
总结
变式

#define _CRT_SECURE_NO_WARNINGS
//头文件
#include<string>#include<cctype>#include<iostream>#include<assert.h>using namespace std;class Student{public:// 1. 构造函数:初始化姓名和年龄Student(const char* name = "", int age = 0){_age = age;_name = new char[strlen(name) + 1];// 给姓名分配内存并拷贝strcpy(_name, name);}// 2. 析构函数:释放姓名的内存~Student(){delete[]_name;_name = nullptr;int age = 0;}// 3. 拷贝构造函数:用已有学生复制新学生Student(const Student& stu){// 新学生的姓名也需要独立内存_age = stu._age;// 为新对象分配自己的内存_name = new char[strlen(stu._name) + 1];// 复制内容strcpy(_name, stu._name);}// 4. 赋值运算符:把s的信息赋给当前对象Student& operator=(Student& stu){// 防止自我赋值(比如 s1 = s1;)if (this != &stu){// 复制年龄和姓名delete[]_name;_age = stu._age;_name = new char[strlen(stu._name) + 1];strcpy(_name, stu._name);return *this;}}// 5. 打印函数:输出学生信息void print() const{std::cout << "姓名:" << _name << ",年龄:" << _age << std::endl;}private:char* _name; // 姓名(动态分配内存)int _age; // 年龄// 测试函数};void testStudent() {std::cout << "===== 创建 s1 =====" << std::endl;Student s1("张三", 18);s1.print();std::cout << "\n===== 拷贝构造创建 s2 =====" << std::endl;Student s2(s1);s2.print();std::cout << "\n===== 赋值创建 s3 =====" << std::endl;Student s3;s3 = s2;s3.print();std::cout << "\n===== 测试结束,对象即将按顺序释放 =====" << std::endl;}int main() {testStudent();return 0;}
知识点总结
一、&既然修改原始值,那么是否还会调用默认构造?
问题:
针对于我们上述的代码,既然我们定义了
char& string::operator[](size_t pos)
但是原构造函数 成员内部有
strcpy(_str,str);
既然都有引用了,是否还调用strcpy重新拷贝,如果拷贝那不就是纯粹浪费空间和时间?如果不拷贝,为什么,难道初始化不走构造函数嘛?
先下结论,operator[] 的调用不会触发构造函数,strcpy 也不会被再次执行
原因是 我们只有在定义 string s1(“hello world”)这个时候他才会调用构造函数,而调用完构造函数之后,他就会存储起来。当我们operator[]时候,就会引用那么我们存储起来的函数。
解释:
对象的生命周期:从生到死一个对象的生命周期可以分为三个阶段:
1.诞生 (Construction):当你创建一个对象时(比如 bit::string s(“hello world”);),构造函数被唯一一>次调用。此时,对象的内存被分配,成员变量被初始化。
2.存活 (Lifetime):对象被创建后,就进入了它的 “存活期”。在这个期间,你可以调用它的各种成员函数来使用或修改它,比如 s.c_str()、s[0] = ‘H’ 等。这个阶段不会再调用构造函数。
3.销毁 (Destruction):当对象即将被销毁时(比如离开它的作用域),它的析构函数被唯一一次调用。此时,对象会清理它所拥有的资源(比如 delete[] _str;)。
二、构造函数就一个 但是却能string 也能const string?
我们的构造函数只写了一个 string(const char* str=“”)
但是我们可以用它来创建两种对象
1.bit::string s1(“hello”)//非const 对象
2.const bit::string s2 (“world”)//const对象这是因为 构造函数它的作用只是限定而非真正的定义就像string(const char* str=“”),如果传入的有参 那么他就不会取缺省值。如果传入的没参 那它就会自动取缺省值。 const 同理,如果定义的为非const那么他就是一个非const对象。如果定义的为const 那么这里的const 就起到作用。
因此这里可以看出另一个问题就是我上述代码在函数定义和声明时,
c_str() 和 operator[]都没有添加后缀const 也就是他们都是非const 成员函数。那么倘若我们这样定义就会报错,原因就是c_str 和operator都是非const成员函数
const bit::string cs("hello");
cout << cs.c_str() << endl; // 编译错误!'c_str' is not a const member function
cs[0] = 'H'; // 编译错误!'operator[]' is not a const member function
最好的做法就是都加上const,但是前提是你不修改原函数这里加上const 还有一点好处就是 const函数可以访问非const 函数和const函数。但是如果不加const,那么只能访问非const函数
总结:
1.构造不分const 和非const 因此在设置缺省值建议加上const
2.1非const 函数无法访问const修饰的函数但是可以修改。
2.2const函数可以访问const修饰的函数,和非const修饰的函数。我们在书写时往往都加上const
3.const书写是C++编程中一个非常重要的规范
三、.1_capcity、_size书写与扩容问题、具体如何区分什么时候该括_capacity_size 以及扩多少?
首先我们来聊聊,他们与/0的关系
_capacity、_size 以及strlen都不包括\0,那么什么时候才会涉及\0?
答案就是在在用new 创建空间时,这个时候如果创建空间必须要创建 capacity+1的空间,因为需要储存\0。所以需要注意 在涉及insert插入一个字符或者push_back 时候 _capacity 往往需要+=2
_capacity _size strlen 什么时候需要扩容 _capacity? 什么时候需要更新 _size? strlen有什么用?
strlen 永远不需要更新 扩容,并且 strlen没有_capacity 和 _size 使用的相对频繁。 strlen的功能完全可以被_size 替代。
补充:strlen 是一个库函数,用于计算C 风格字符串的有效长度(即从首字符到 ‘\0’ 前的字符数)。
它是一个 “临时计算” 的函数,不是类的成员变量,因此不存在 “更新” 的说法。
_size 本质上就是对 strlen 结果的缓存(因为每次字符串修改后,_size 都会同步更新),这样可以避免频繁调用 strlen 带来的性能开销。
小总结:
在插入,删除,扩容,以及增删改除后 _size 和_capacity 都需要我们重新更新
扩容往往不需要对_size 进行更新 只需要对_capacity 进行更新
在插入 增加指定n个字符时 _size 往往需要更新 _capacity 也需要更新
.2 new char[_capacity+1] 会不会扩容_capacity? strcpy(_str,str)之后会不会自动改变_size?
结论 new char之后不会改变_capacity容量,strcpy之后也不会改变_size的大小
new char 只负责内存的创建,它完全不认识你参数里面的_capacity 是什么东西
strcpy也同理 它只负责拷贝,它根本就不管你里面有什么_size _capacity strlen 还是其他什么东西
因此他们不会对_capacity 和 _size 私有成员进行改变,在最后我们需要更新_capacity _size的时候还得手动更新3. delete[]_str 之后能否进行 strcpy(_str,str)?
结论是不能
delete[]_str 之后,_str指向的资源被释放,那么现在_str只剩一个空地址,它的_capacity 和_size都已经成为随机值(由于释放),而strcpy是需要对地址进行解引用,直到遇到\0位置。这里如果strcpy的话就会出现随机值访问的错误
四、C语言的for循环在更新数据的弊端
在没有\0、或者有空间限制的情况下 需要特别注意\0的添加
否则会发生下列问题
这种后面是垃圾值,原因是结尾没有/0,但是也没有发生越界
还有一种情况是烫烫烫,这种是访问到了随机值,就是这个空间没有进行赋值,是空的。那么访问时就会出现烫烫烫。 这种也没有发生越界。但都是两种错误的典范。
