string 类中的几个内置函数,容易遗忘
1.clear()清空有效字符
2.erase()删除某一个位置的字符,就和在固定位置进行数组元素的删除差不多
3.insert(),在固定位置插入元素,或者字符串
4.assign()截断,和substr()差不多
5.replace()替换,字符串的替换
strcpy是如何实现的
char* a = "12345";
char* b = new char[6];
char* c = b;
int count = 0;
while (*c++ = *a++) //在最后一次循环的时候是将'\0'赋值给*c之后发现,赋值‘\0’也就是空,也就跳出了循环但是此时实际上'\0'时已经赋值成功了的
//也就是说strcpy是会拷贝最后的‘\0’的
count++; //可以发现总共进行了五次循环,
cout << count << endl;
编译器提供的默认的拷贝构造函数,会造成浅拷贝
编译器提供的默认的赋值运算符也是浅拷贝,而且会造成内存泄漏
浅拷贝的后果就是,如果对象设置到资源的管理,多个对象共用同一份资源,释放对象的时候,会导致一份资源被释放多次,而引起的代码崩溃。
深拷贝方式:再拷贝构造和赋值的时候,让每个对象独立的有用一份资源
//经典版的string,但是代码复用率比较低
namespace n1
{
class string
{
friend ostream& operator<<(ostream& _cout, const string& p);
public:
string(char* str = "") //默认的构造函数,给出了缺省参数为的是防止只声明 不初始化
{
if (str == nullptr) //其次就是判空假如外部传递的参数是空的话,
str = ""; //那就先让指针指向""空字符串再拷贝,最后的结果就是只拷了"\0"
_str = new char[strlen(str) + 1]; //开辟空间
strcpy(_str, str); //拷贝元素
}
string(const string& p)
{
_str = new char[p.size() + 1];
strcpy(_str, p._str);
}
string& operator=(const string& p)
{
if (this != &p) //注意这里比较的是地址
{
delete[] _str; //刚开始我还在这一步进行了判空操作,最后发现没有必要因为,因为所有对象在初始化的时候都有空间,哪怕是个'\0'
_str = new char[p.size() + 1];
strcpy(_str, p._str);
}
return *this;
}
int size()const
{
return strlen(this->_str);
}
int size()
{
return strlen(this->_str);
}
char operator[](int x)const
{
return _str[x];
}
~string()
{
if (_str)
{
delete[] _str;
_str = nullptr;
}
}
private:
char* _str;
};
ostream& operator<<(ostream& _cout, const string& p)
{
for (int i = 0; i < p.size();++i)
{
_cout << p[i];
}
return _cout;
}
void test()
{
string s("123");
string s1(nullptr);
string s2(s);
cout << s2 << endl;
s2 = "456";
cout << s2 << endl;
}
}
using namespace n1;
int main()
{
test();
return 0;
}
简洁版的代码(只将和上面不同的的代码部分列出):
string(char* str = "") //默认的构造函数,给出了缺省参数为的是防止只声明 不初始化
{
if (str == nullptr) //其次就是判空假如外部传递的参数是空的话,
str = ""; //那就先让指针指向""空字符串再拷贝,最后的结果就是只拷了"\0"
_str = new char[strlen(str) + 1]; //开辟空间
strcpy(_str, str); //拷贝元素
}
//但是在此处 又有一个问题就是,vs2013将我们设置的_str,没有初始化的情况下会给成nullptr
//但是别的编译器就会不一定设置为什么
//假如现在的_str里面放的是随机值,那么和临时对象交换之后,相当于临时对象指向的是随机值,那么在析构临时对象的时候
//假如发现不是指向空,而是随机值,而去释放那么代码就崩溃了,但是要是时空的话,就不会去释放,就没事
string(const string& p)
:_str(nullptr) //所以先制空
{
string strtemp(p._str); //使用默认构造函数来构造拷贝构造函数,提高代码复用率,但是这里需要注意的是,使用的是p._str,而不是直接使用对象,否则就是无限递归
swap(_str, strtemp._str); //交换临时变量和当前对象
}
string& operator=(string p) //直接在 传参阶段就调用拷贝构造函数构造出临时对象
{
//这个版本根本不用考虑自己给自己赋值,因为就算你是自己给自己赋值,赋值之后还是自己
swap(_str, p._str);
return *this;
}
上的代码只是在复用方面给出了优化,都用到了临时变量,拷贝构造函数利用的是构造函数,创建临时对象,然后和临时交换即可,临时对象出了作用域就会被释放,需要注意的就是在使用临时对象之前要在参数列表中将构造对象中的指针制空,怕的是交换之后,在析构函数中释放临时对象的时候,不同的编译器会出现问题。
其实针对浅拷贝还有解决办法,既然浅拷贝所爆发的问题就是,在最后释放资源的时候,因为资源释放了多次,才造成
了错误,所以在一个资源有多个对象的时候,可以让最后一个释放该份资源的对象将资源彻底释放
计数解决浅拷贝
namespace n1
{
class string
{
friend ostream& operator<<(ostream& _cout, const string& p);
public:
string(char* str = "")
:count(new int(1)) //每一个新的资源都是通过构造函数诞生的,所在构造函数的位置多申请一块空间,这空间被共享这块资源的所有对象都可以范围,注意前题是所有共享这一块资源的对象
{
if (str == nullptr)
str = "";
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
string(const string& p) //拷贝构造就是直接让新 对象的指针全部指向,被共享的资源,只需要使资源计数+1即可
:_str(p._str)
, count(p.count)
{
++(*count);
}
string& operator=(const string& p)
{
if (this != &p)
{
//if (_str && 0 == (*count)--) //如果现在
//{
// delete[] _str;
// delete count;
// count = nullptr;
// _str = nullptr;
//}
this->~string(); //其实我感觉这里直接调用析构函数,可以增加代码复用性
_str = p._str;
count = p.count;
(*(p.count))++;
}
return *this;
}
int size()const
{
return strlen(this->_str);
}
int size()
{
return strlen(this->_str);
}
char operator[](int x)const
{
return _str[x];
}
~string()
{
if ((_str!=nullptr) && (0 == --(*count))) //简写为if(_str && 0 == --(*count))
{
delete[] _str;
delete count;
count = nullptr;
_str = nullptr;
}
}
private:
char* _str;
int* count;
};
ostream& operator<<(ostream& _cout, const string& p)
{
for (int i = 0; i < p.size(); ++i)
{
_cout << p[i];
}
return _cout;
}
void test()
{
string s("123");
string s2(s);
cout << s2 << endl;
s2 = "456";
string s3 = s2;
cout << s2 << endl;
}
}
using namespace n1;
int main()
{
test();
return 0;
}
但是但是但是上面的代码在解决人家[ ]访问操作,然后进行对单独对象修改的时候,又存在一个写时拷贝技术,呃呃呃呃,算了算了,
我给代码里加入了下面代码,算是解决了写是拷贝吧,重载了[ ]
char& operator[](int x) //注意返回的是引用
{
if(*(this->count) == 1)
{
return this->_str[x];
}
else
{
char * temp1 = new char[strlen(this->_str) + 1];
strcpy(temp1, _str);
int * temp2 = new int(1);
this->~string();
this->count = temp2;
this->_str = temp1;
return this->_str[x];
}
}
//可以在加入了上面这段代码的类中调试,测试
void test()
{
string s("123");
string s2(s);
s2 = "456";
string s3 = s2;
s2[0] = 'w'; //重载的就是这一步
cout << s2 << endl;
}