问题示范
首先放上的是问题代码,也是容易写出的代码:
#include<iostream>
using namespace std;
class String{
private:
char *data;
int n=0;
public:
String(const String &s){
n=s.n;
for(int i=0;i<n;i++){
data[i]=s.data[i];
}
data[n]=s.data[n];
}
String(const char* s=0){
if(s==0){
data=0;
return;
}
const char *p=s;
for(n=0;*p!='\0';p++,n++);
data=new char[n+1];
for(int i=0;i<=n;i++){
data[i]=s[i];
}
}
~String(){
cout<<"析构";
if(data){
delete[] data;
}
}
int length(){
return this->n;
}
void push_back(char addData){
this->data[n]=addData;
n+=1;
this->data[n]='\0';
}
char pop_back(){
char res=this->data[--(this->n)];
this->data[n]='\0';
return res;
}
int pop_forward(){
return 1;
}
char operator [] (int th)const{
if(th>=n)
throw "IOException";
if(th<0)
return this->data[n+th];
return this->data[th];
}
char& operator [] (int th){
if(th>=n)
throw "IOException";
if(th<0)
return this->data[n+th];
return this->data[th];
}
friend ostream &operator << (ostream &out,String string);
};
ostream &operator << (ostream &out,String string){
for(int i=0;string.data[i];i++)
out<<string.data[i];
out<<endl;
return out;
}
int main(void){
String str2("hello world");
cout<<str2<<"num:"<<str2.length()<<endl;
str2[1]='g';
cout<<str2[-1];
return 0;
}
测试的有效输出完全没有产生,所有输出都失败了。
/tmp/E0tBOrQajU.o
Segmentation fault
=== Code Exited With Errors ===
错误原因
发生错误是由于在进行输出时复制了一次str2,这个操作是在运算符重载的函数中进行的,它直接拷贝了str2的地址,导致析构函数被调用了两次,而第二次时已经没有内存可以释放了。
明确了错误发生的原因后,我们就可以对其进行解决了,可以想到两种解决的办法,一种是想办法使析构函数只调用一次,另一种是使析构函数成功调用第二次,下面对两种方法分别进行实现。
减少一次析构函数的调用
重载运算符
在重载运算符函数进行修改,直接传入对象的引用,这样在复制过程中(参数传递时发生的)就不会构造新的对象。
ostream &operator << (ostream &out,String &string){
for(int i=0;string.data[i];i++)
out<<string.data[i];
out<<endl;
return out;
}
友元函数
类中的友元函数部分也需要进行修改。
friend ostream &operator << (ostream &out,String &string);
输出结果
观察输出结果,确实只调用了一次析构函数,而且是成功的。
/tmp/tIgOwcixXJ.o
hello world
num:11
d析构=== Code Execution Successful ===
使析构函数成功调用第二次
拷贝函数
关键在于将拷贝函数进行更换,改为深层拷贝的方法,为data分配新的内存空间。
String(const String &s){
data=new char[s.n+1];
n=s.n;
for(int i=0;i<=n;i++){
data[i]=s.data[i];
}
}
输出结果
观察输出结果,发现析构函数确实调用了两次,两次调用均成功,没有引发错误。
/tmp/tKr6f45BwY.o
hello world
num:11
析构d析构=== Code Execution Successful ===