目录
1.深浅拷贝问题
问题代码:
//C++标准库命名空间最大的意义,解决定义一个和库函数名字一样的类也是可以的
namespace sakeww
{
class string
{
public:
string(const char* str)
:_str(new char[strlen(str) + 1])
{
strcpy(_str, str);
}
~string()
{
delete[] _str;
_str = nullptr;
}
private:
char* _str;
};
void test_string1()
{
string s1("hello string!");//没问题
string s2(s1);//出现问题了
}
}
问题和解答:
这段代码是有问题,为什么?思考一下,然后再看下面解析
答:
-
s1自己创建了一个空间,里面放了hello string!
-
拷贝构造是默认函数,我们不写,编译器会默认生成一份
生成的会对内置类型进行值拷贝(浅拷贝)
自定义类型会去调用他的拷贝构造 -
s2会将s1中的每个字节拷贝到s2
也就是将s1中的每个指针拷贝到s2中,相当于s2._str 指向了s1._str
-
在执行析构函数的时候,s1释放了_str,s2不能释放空空间,所以出现错误
解决:
进行深拷贝
namespace sakeww
{
class string
{
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;
}
private:
char* _str;
};
void test_string1()
{
string s1("hello string!");
string s2(s1);
}
}
2.=
问题代码:
string& operator=(const string& s)
{
delete[] _str;
_str = new char[strlen(s._str + 1)];
strcpy(_str, s._str);
return *this;
}
问题与解答:
这是一个模拟=的代码
当想要实现s1 = s1时,这个就会报错
解决:
起始这个问题的问题就是 开局直接delete,导致后面的赋值不了
两种写法:
string& operator=(const string& s)
{
//第一种直接判断是否相等,然后在做决定
//if (this != &s) {
// delete[] _str;
// _str = new char[strlen(s._str + 1)];
// strcpy(_str, s._str);
//}
//这一块的问题也就是最开始空间释放了,然后拷贝这个空间的内容,我们新创建一个空间
char* temp = new char[strlen(s._str + 1)];
strcpy(_str, s._str);
delete[] _str;
_str = temp;
return *this;
}
3.定义空string
问题代码:
namespace sakeww
{
class string
{
public:
string()
:_str(nullptr)
{}
string(const char* str)
:_size(strlen(str))
,_capacity(_size)
{
_str = new char[_capacity + 1];
strcpy(_str, str);
}
string(const string& s)
:_size(s._size)
, _capacity(s._capacity)
{
_str = new char[_capacity + 1];
strcpy(_str, s._str);
}
const char* c_str()
{
return _str;
}
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
private:
char* _str;
size_t _size;
size_t _capacity;//有效字符空间数,不算\0
};
void test_string1()
{
string s1("hello string!");
string s2;
cout << s1.c_str() << endl;
}
}
问题与解答:
为什么会崩溃?
s2调用的是默认构造函数,
默认构造函数给个空是不行的
这是stl中的string
内容是空
size是0
capacity是15
解决:
namespace sakeww
{
class string
{
public:
string(const char* str = "")
:_size(strlen(str))
,_capacity(_size)
{
_str = new char[_capacity + 1];
strcpy(_str, str);
}
string(const string& s)
:_size(s._size)
, _capacity(s._capacity)
{
_str = new char[_capacity + 1];
strcpy(_str, s._str);
}
const char* c_str()
{
return _str;
}
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
private:
char* _str;
size_t _size;
size_t _capacity;//有效字符空间数,不算\0
};
void test_string1()
{
string s1("hello string!");
string s2;
cout << s1.c_str() << endl;
cout << s2.c_str() << endl;
}
}
4.范围for问题
for(auto e : s1)
{
cout << e << " ";
}
cout << endl;
范围for是基于迭代器实现的
他会去直接找你实现的sakeww::string中的begin和end
如果你的模拟实现中begin和end的名字是其他的,那么这个返回for就会执行不了
5.深拷贝和赋值(现代写法)
传统写法:深拷贝
赋值:
现代写法:
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
string(const string& s)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
string tmp(s._str);
swap(tmp);
}
string& operator=(string& s)
{
swap(s);
return *this;
}
6. insert插中,越界问题
正确代码:
string& insert(size_t pos, const char* s)
{
assert(pos <= _size);
size_t len = strlen(s);
if (_size + len > _capacity)
{
reserve(_size + len);
}
size_t end = _size + len;
//在头插中,pos=0,end在遍历时,一直是大于零的,因为只需要插入len个字符,所以在前面len个位置就不用end--了
while (end >= pos+len)
{
_str[end] = _str[end - len];
--end;
}
strncpy(_str + pos, s, len);
_size += len;
return *this;
}
7. 比较运算符重载成全局函数
因为我们已经将[],重载,所以我们可以重载成全局函数
c_str已经重载,所以可以使用c语言的一些函数
bool operator<(const string& s1, const string& s2)
{
//size_t i1 = 0, i2 = 0;
//while (i1 < s1.size() && i2 < s2.size())
//{
// if (s1[i1] < s2[i2])
// {
// return true;
// }
// else if (s1[i1] > s2[i2])
// return false;
// else
// {
// ++i1;
// ++i2;
// }
//}
当两个字符其中一个结束后
//return i2 < s2.size() ? true : false;
//运用strcmp
return strcmp(s1.c_str(), s2.c_str()) < 0;
}
8.重载<< 之 为什么不能用out<<s.c_str()?
这种方式不符合规范
范围for是根据size来确定输出的
s.c_str()遇见\0就会停止,如果字符中其中一个字符数\0在中间,那么就会停止
9.关于浅拷贝
如果非要浅拷贝可以运用引用计数的方法。
浅拷贝的问题和解决:
- 析构两次
当第二个变量指向同一块空间,那么就将引用计数++
当析构时,就将引用计数– - 其中一个对象进行修改会影响其中一个。
当需要对其进行修改的时候,然后再进行深拷贝,然后进行修改(延迟拷贝思想)
缺陷:
- 引用计数存在线程安全问题,需要加锁,在多线程环境下要付出代价
- 在动态库和静态库有些场景会有问题
10.string的模拟实现
//C++标准库命名空间最大的意义,解决定义一个和库函数名字一样的类也是可以的
namespace sakeww
{
class string
{
public:
//迭代器
typedef char* iterator;
typedef const char* const_iterator;
const_iterator begin()const
{
return _str;
}
const_iterator end()const
{
return _str + _size;
}
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
string(const char* str = "")
:_size(strlen(str))
,_capacity(_size)
{
_str = new char[_capacity + 1];
strcpy(_str, str);
}
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
//传统写法:
//string(const string& s)
// :_size(s._size)
// , _capacity(s._capacity)
//{
// _str = new char[_capacity + 1];
// strcpy(_str, s._str);
//}
//现代写法
string(const string& s)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
string tmp(s._str);
swap(tmp);
}
//s1=s3传统写法:
//string& operator=(const string& s)
//{
// //if (this != &s) {
// // delete[] _str;
// // _str = new char[strlen(s._str + 1)];
// // strcpy(_str, s._str);
// //}
// char* temp = new char[s._capacity + 1];
// strcpy(temp, s._str);
// delete[] _str;
// _str = temp;
// _size = s._size;
// _capacity = s._capacity;
// return *this;
//}
//现代写法:
string& operator=(string& s)
{
swap(s);
return *this;
}
const char* c_str()const
{
return _str;
}
size_t size()const
{
return _size;
}
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
//reserve对容量进行改变
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n+1];
strcpy(tmp,_str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void resize(size_t n, char ch = '\0')
{
if (n <= _size)
{
_size = n;
_str[_size] = '\0';
}
else
{
if (n > _capacity)
{
reserve(n);
}
memset(_str + _size, ch, n - _size);
_size = n;
_str[_size] = '\0';
}
}
void push_back(char ch)
{
//if (_size == _capacity)
//{
// reserve(_capacity == 0 ? 4: _capacity * 2);
//}
//_str[_size] = ch;
//++_size;
//_str[_size++] = '\0';
insert(_size, ch);
}
void append(const char* str)
{
//size_t len = strlen(str);
//if (_size + len > _capacity)
//{
// reserve(_size + len);
//}
//strcpy(_str + _size, str);
//_size += len;
insert(_size, str);
}
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
size_t find(char ch)
{
for (size_t i = 0; i < _size; ++i)
{
if (ch == _str[i])
return i;
}
return npos;
}
size_t find(const char* s, size_t pos = 0)
{
const char* ptr = strstr(_str + pos, s);
if (ptr == nullptr)
{
return npos;
}
else
{
return ptr - _str;
}
}
string& insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
/*
如果pos是零,那么--end = -1(最大)
size_t end = _size;
while (end >= pos)
{
_str[end + 1] = _str[end];
--end;
}*/
/*
int end = _size;
while (end >= (int)pos)
{
_str[end + 1] = _str[end];
--end;
}*/
size_t end = _size + 1;
while (end > pos)
{
_str[end] = _str[end - 1];
--end;
}
_str[pos] = ch;
++_size;
return *this;
}
string& insert(size_t pos, const char* s)
{
assert(pos <= _size);
size_t len = strlen(s);
if (_size + len > _capacity)
{
reserve(_size + len);
}
size_t end = _size + len;
while (end >= pos+len)
{
_str[end] = _str[end - len];
--end;
}
strncpy(_str + pos, s, len);
_size += len;
return *this;
}
//从pos位置,删除npos个字符
string& erase(size_t pos = 0, size_t len = npos)
{
assert(pos < _size);
if (len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return *this;
}
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
void clear()
{
_str[0] = '\0';
_size = 0;
}
private:
char* _str;
size_t _size;
size_t _capacity;//有效字符空间数,不算\0
public:
static const size_t npos = -1;
};
bool operator<(const string& s1, const string& s2)
{
//size_t i1 = 0, i2 = 0;
//while (i1 < s1.size() && i2 < s2.size())
//{
// if (s1[i1] < s2[i2])
// {
// return true;
// }
// else if (s1[i1] > s2[i2])
// return false;
// else
// {
// ++i1;
// ++i2;
// }
//}
当两个字符其中一个结束后
//return i2 < s2.size() ? true : false;
//运用strcmp
return strcmp(s1.c_str(), s2.c_str()) < 0;
}
bool operator==(const string& s1, const string& s2)
{
return strcmp(s1.c_str(), s2.c_str()) == 0;
}
bool operator<=(const string& s1, const string& s2)
{
return (s1 < s2) || (s1 == s2);
}
bool operator>(const string& s1, const string& s2)
{
return !(s1 <= s2);
}
bool operator!=(const string& s1, const string& s2)
{
return !(s1 == s2);
}
ostream& operator<<(ostream& out,const string& s)
{
for (auto ch : s)
{
out << ch;
}
return out;
}
istream& operator>>(istream& in,string& s)
{
s.clear();
char ch = in.get();
while (ch != ' ' && ch != '\n')
{
s += ch;
ch = in.get();
}
return in;
}
void test_string1()
{
string s1;
cin >> s1;
cout << s1 << endl;
}
}