怎么重载+,使s1+s2在输出后s1和s2不被改变。

时间:2022-09-06 20:10:59
 

//string2.h 
#define STRING2_H 
#include <iostream.h> 
class String{ 
friend ostream &operator < <(ostream &,const String &); 
public: 
String(const char * ="" ); 
~String(); 
    String operator+(const String &); 
const String &operator=(const String &); 
private: 
int length; 
char *sptr; 
void setString(const char *); 
}; 
#endif 
//string2.cpp 
#include <iostream.h> 
#include <iomanip.h> 
#include <string.h> 
#include <assert.h> 
#include"string2.h" 
String::String(const char *s):length(strlen(s)) 

cout < <"conversion constructor: " < <s < <" a\n"; 
setString(s); 

String::~String() 

cout < <"destructor: " < <sptr < <'\n'; 
delete [] sptr; 

void String::setString(const char *string2) 

sptr=new char[length+1]; 
assert(sptr!=0); 
strcpy(sptr,string2); 

String String::operator +(const String &right) 

char *tempptr;      
length+=right.length; 
tempptr=new char[length+1]; 
        strcpy(tempptr,sptr);    
strcat(tempptr,right.sptr); 
cout < <" b\n"; 
return tempptr; 

const String &String::operator =(const String &right) 

cout < <"operator=called\n"; 
if(&right!=this){ 
delete [] sptr; 
length=right.length; 
setString(right.sptr); 

else 
cout < <"attempted assingnement of a string to itself\n"; 
return *this; 

ostream &operator < <(ostream &output,const String &s) 

output < <s.sptr; 
return output; 

//main().cpp 
#include <iostream.h> 
#include"string2.h" 
int main() 

String s1("happy"),s2(" birthday"),s3(" to you."); 
//这程序的目的是在输出s1=s2+s3时不改变s2和s3(书本上的例子是重载+=), 
//我在原来operator +=(const String &right)的基础上修改出了现在的operator +(const String &right) 
//下面是原来的operator +=(const String &right) 
//const String &String::operator +=(const String &right) 
//{ 
// char *tempptr=sptr;    
// length+=right.length; 
// sptr=new char[length+1]; 
// assert(sptr!=0); 
// strcpy(sptr,tempptr);  
// strcat(sptr,right.sptr); 
// delete [] tempptr; 
// return *this; 
//} 
//看了输出,上面的调用3次构造函数,表示理解,但是在输出"s1 is "前居然4次调用operator +(const String &right) 
//而且还多调用了4次构造函数和1次operator =(const String &right),这些是我想不明白的。 
//麻烦各位高手说下如何重载“+”才正确。 
cout < <"s1 is " < <s1 
< <"\ns2 is " < <s2 
< <"\ns3 is " < <s3//还没到s3=s1+s2就帮我提前修改了s3,想不通为什么。 
< <"\ns1+s2 is " < <s1+s2 
< <"\ns1+s2+s3 is " < <s1+s2+s3 
< <"\ns3=s1+s2 is " < <(s3=s1+s2) < <endl; 
return 0; 

37 个解决方案

#1


String operator+(const String &);  
是这样写的.

#2


一般来说, +重载为友元更好. 
friend String operator+(const String &str1, const String &str2) {
    char *result = new char[strlen(str1.size()) + strlen(str2.size()) + 1];
    strcpy(result, str1.pstr);
    strcat(result, str2.pstr);

    String rs(result);
    delete result;

    return rs;
}

#3


String String::operator +(const String &right1, const String &right2)
+是双目运算

#4


String String::operator +(const String &right)  
{  
char *tempptr;       
length+=right.length;  
tempptr=new char[length+1];  
        strcpy(tempptr,sptr);     
strcat(tempptr,right.sptr);  
cout  <  <" b\n";  
return tempptr;  
}
不能返回局部变量的指针,局部变量在函数的尾部就被删除了,返回局部变量的指针是没有意义的。

#5


回2楼,我按照你说的改了。程序可以运行。但是达不到想要的效果。
回3楼,厄~我看书上说对于双目运算符有2种重载:
1是只有1个参数的类成员函数。试用于2个对象都是类对象。
2是有2个参数。试用于有1个对象不是类对象。就像重载<<.
如果String String::operator +(const String &right1, const String &right2)行
麻烦你可以发发代码怎么实现吗?
我想学习学习,顺便解决我的问题。


最后,希望各位高手帮帮忙~

#6


+是双目运算,但成员函数隐式传递this指针,所以可以这样定义:
String operator+(const String &);   

#7


好久没看C++代码,现在一看真是有点熟悉而又一些陌生。

#8


回4楼,我把char *tempptr声明为private了,还是不行,而且跟原来的输出没什么区别。
可以说清楚点不?

#9


s1+s2要产生一个临时对象,所以也要调用构造函数,当输出s1+s2后,就调用析构函数删除临时对象
s1+s2+s3要产生两个临时对象,一个是s1+s2,一个是s1+s2+s3。
//string2.h
#ifndef STRING2_H  
#define STRING2_H  
#include  <iostream.h>  
class String{  
friend ostream &operator  <<(ostream &,const String &);  
public:  
String(const char * ="" );  
~String();  
String operator+(const String &);  
const String &operator=(const String &);  
private:  
int length;  
char *sptr;  
void setString(const char *);
char *tempptr;  
};  
#endif 
#include  <iostream.h>  
#include  <iomanip.h>  
#include  <string.h>  
#include  <assert.h>  
#include  "string2.h"  
String::String(const char *s):length(strlen(s))  
{  
cout  <<"conversion constructor: "  <<s  <<" a\n";  
setString(s);  
}  
String::~String()  
{  
cout  <<"destructor: "  <<sptr  <<'\n';  
delete [] sptr;  
}  
void String::setString(const char *string2)  
{  
sptr=new char[length+1];  
assert(sptr!=0);  
strcpy(sptr,string2);  
}  
String String::operator +(const String &right)  
{  
       
length+=right.length;  
tempptr=new char[length+1];  
        strcpy(tempptr,sptr);     
strcat(tempptr,right.sptr);  
cout  <<" b\n";  
return tempptr;  
}  
const String &String::operator =(const String &right)  
{  
cout  <<"operator=called\n";  
if(&right!=this){  
delete [] sptr;  
length=right.length;  
setString(right.sptr);  
}  
else  
cout  <<"attempted assingnement of a string to itself\n";  
return *this;  
}  
ostream &operator  <<(ostream &output,const String &s)  
{  
output  <<s.sptr;  
return output;  
}   
int main()  
{  
String s1("happy"),s2(" birthday"),s3(" to you.");  
//String s=s1+s2; 
cout  <<"s1 is "<<s1<<endl;  
cout<<"\ns2 is "<<s2<<endl;  
cout<<"\ns3 is "<<s3<<endl;//还没到s3=s1+s2就帮我提前修改了s3,想不通为什么。  
cout<<"\ns1 add s2 is "<<s1+s2<<endl;  
cout<<"\ns1 add s2 add s3 is "<<s1+s2+s3<<endl;  
cout<<"\ns3=s1+s2 is "<<(s3=s1+s2)<<endl;  
return 0;  

输出结果为:
conversion constructor: happy a
conversion constructor:  birthday a
conversion constructor:  to you. a
s1 is happy

s2 is  birthday

s3 is  to you.
 b
conversion constructor: happy birthday a

s1 add s2 is happy birthday
destructor: happy birthday
 b
conversion constructor: happy birthday a
 b
conversion constructor: happy birthday to you. a

s1 add s2 add s3 is happy birthday to you.
destructor: happy birthday to you.
destructor: happy birthday
 b
conversion constructor: happy birthday a
operator=called

s3=s1+s2 is happy birthday
destructor: happy birthday
destructor: happy birthday
destructor:  birthday
destructor: happy
Press any key to continue

#10


String String::operator +(const String &right)  
{  
static char *tempptr;       
length+=right.length;  
tempptr=new char[length+1];  
        strcpy(tempptr,sptr);     
strcat(tempptr,right.sptr);  
cout   <   <" b\n";  
return tempptr;  

这样试试~

#11


正确的应该是
String operator+(const String &)const;   
引用 1 楼 akirya 的回复:
String operator+(const String &);   
是这样写的.

#12


这种东西没有任何理由重载为友元,为什么它是友元更好?
一般来说,友元只有在第一个参数不是自己类型时才用如
friend String operator+(LPCTSTR str1, const String& str2);
对于第一个参数是同一个类的引用时,友元没有任何好处
引用 2 楼 Inhibitory 的回复:
一般来说, +重载为友元更好.  
friend String operator+(const String &str1, const String &str2) { 
    char *result = new char[strlen(str1.size()) + strlen(str2.size()) + 1]; 
    strcpy(result, str1.pstr); 
    strcat(result, str2.pstr); 

    String rs(result); 
    delete result; 

    return rs; 
}

#13


不知道你怎么会达不到效果,下面代码很容易做到

String String::operator+(const String & str)const
{
      String res;
      res.length = length + str.length;
      res.sptr = new char[res.length+1];
      strcpy(res.sptr,sptr);
      strcat(res.sptr,str.sptr);
      return res;     
}

#14


这个不是返回局部变量指针,只要它有正确的拷贝构造函数和operator=即可
因为这个函数返回的对象必然是临时的,因此接收此返回值的表达式必须有clone这个对象的能力,这就必须实现拷贝构造和operator=
引用 4 楼 coverallwangp 的回复:
String String::operator +(const String &right)   
{   
char *tempptr;        
length+=right.length;   
tempptr=new char[length+1];   
        strcpy(tempptr,sptr);      
strcat(tempptr,right.sptr);   
cout   <   <" b\n";   
return tempptr;   

不能返回局部变量的指针,局部变量在函数的尾部就被删除了,返回局部变量的指针是没有意义的。 

#15


String operator+(const String &)const; 
后面的const是使this的指针变为const
调用该函数的对象是只读的,不能改变它.
所以想调用s1+s2不变,应该在后面加const

#16


回13楼,真的不行。弹出一个对话框,有 终止 重试 取消 之类的。就想运行非法一样。

#17


下面这个函数是很不对的

void String::setString(const char *string2)  
{  
sptr=new char[length+1];  
assert(sptr!=0);  
strcpy(sptr,string2);  
}  


这个函数总是假定length已经提前设置好了,这个把一个类的各个属性分开设置的方法违背了“consistency”的原则
应该这样设计才对


void String::setString(const char *string2)  
{  
    length = strlen(string2);
    sptr=new char[length+1];  
    strcpy(sptr,string2);  
}  

#18


那不是这个函数的原因,而是你没有实现拷贝构造
考虑这样的一个式子
String s1("abc");
String s2("def");
String s3 = s1+s2;
由于s1+s2返回一个String对象(注意它是临时对象),当S3接收这个值时,由于你没有实现拷贝构造,系统会非常傻的替你实现一个逐字节拷贝的拷贝构造函数

假定临时对象是_s3,其sptr指向某个new出来的地址,拷贝后,_s3和s3的sptr都指向它

当_s3和s3分别析构,问题就出来了。由于s3, _s3分别通过~String调用delete[]sptr,这必然delete同一个指针两次,从而导致问题。

引用 16 楼 hahaysw 的回复:
回13楼,真的不行。弹出一个对话框,有 终止 重试 取消 之类的。就想运行非法一样。

#19


搞不懂你干吗要单独实现一个setString,而又不把length的赋值操作放在里面干吗?

#20


mark 一个

#21


把String String::operator+(const String & str)const
{
      String res;
      res.length = length + str.length;
      res.sptr = new char[res.length+1];
      strcpy(res.sptr,sptr);
      strcat(res.sptr,str.sptr);
      return res;     
}加进来的确可以,不过要加如下面的构造函数
String::String(const String &copy):length(copy.length)
{                                                   
cout<<"copy constructor: "<<copy.sptr<<'\n';   
setString(copy.sptr);
}
书上说这是一个复制构造函数,它通过复制已存在的String对象来初始化另一对象。

其实我对这2个构造函数也不是完全理解:
String::String(const char *s):length(strlen(s))
说可以把char *转换为类对象。
String::String(const String &copy):length(copy.length)
说通过复制已存在的String对象来初始化另一对象。

有高手可以说下怎么理解不?要不明天我另外开贴~

#22


回19楼,这是受了书上例子的影响。
你有更好的实现方法吗?不建立setString,
让我学习学习,非常感谢~

#23


楼主,我先回答你第一个问题:

cout  <<"s1 is "  <<s1  
<<"\ns2 is "  <<s2  
<<"\ns3 is "  <<s3//还没到s3=s1+s2就帮我提前修改了s3,想不通为什么。  
<<"\ns1+s2 is "  <<s1+s2  
<<"\ns1+s2+s3 is "  <<s1+s2+s3  
<<"\ns3=s1+s2 is "  <<(s3=s1+s2)  <<endl;  

以上原因是因为流是从右向左逐一运算的,所以当你显示s3的时候已经运行完毕s3=s1+s2这个操作了.
加号运算符
另外如何重载运算符,一定要服从著名的BIG THREE原则,即:需要定义析构函数,复制构造函数和复制运算符中任意一个的时候,一定要定义另外两个,你程序中的

<<"\ns1+s2 is "  <<s1+s2  //这部分需要复制构造函数


所以说+运算符重载,也需要考虑到其他问题.
我改好的代码如下:

//string2.h  
 
#include  <iostream.h>  
class String{  
friend ostream &operator  <<(ostream &,const String &);  
public:  
String(const char * ="" );  
String(const String & tempStr);
~String();  
    String operator+(const String &);  
const String &operator=(const String &);  
private:  
int length;  
char *sptr;  
void setString(const char *);  
};  

//string2.cpp  
#include  <iostream.h>  
#include  <iomanip.h>  
#include  <string.h>  
#include  <assert.h>  
#include"string2.h"  
String::String(const char *s):length(strlen(s))  
{  
cout  <<"conversion constructor: "  <<s  <<" a\n";  
setString(s);  
}  
String::~String()  
{  
cout  <<"destructor: "  <<sptr  <<'\n';  
delete [] sptr;  
}  
void String::setString(const char *string2)  
{  
sptr=new char[strlen(string2)+1];  
assert(sptr!=0);  
strcpy(sptr,string2);
}  
String String::operator +(const String &right)  
{  
char *tempptr=new char[length+right.length+1];  
        strcpy(tempptr,sptr);     
strcat(tempptr,right.sptr);  
cout  <<" b\n"; 
String tempStr(tempptr); 
return tempStr;
}  
const String &String::operator =(const String &right)  
{  
cout  <<"operator=called\n";  
if(&right!=this){  
delete [] sptr;  
length=right.length;  
setString(right.sptr);  
}  
else  
cout  <<"attempted assingnement of a string to itself\n";  
return *this;  
}  

ostream&  operator <<(ostream &output,const String &s)  
{  
output  << s.sptr;  
return output;  
}  

String::String(const String & tempStr)
{
length=tempStr.length;
setString(tempStr.sptr);
}

//main().cpp  
#include  <iostream.h>  
#include"string2.h"  
int main()  
{  
String s1("happy"),s2(" birthday"),s3(" to you.");  

cout  <<"s1 is "  <<s1<<endl  ;
cout<<"\ns2 is "  <<s2 <<endl ;
cout<<"\ns3 is "  <<s3<<endl;
cout<<"\ns1+s2 is "  <<s1+s2 <<endl ;
cout<<"\ns1+s2+s3 is "  <<s1+s2+s3<<endl  ;
cout<<"\ns3=s1+s2 is "  <<(s3=s1+s2)  <<endl;  
return 0;  
}  

#24


写一个私有的setString也不是不可以,但是你需要把length的赋值也放到里面去,否则可能会造成对象自身的不一致
假如你写了这个类2年之后,你又加了一个函数如下:


void String::foo()
{
    setString("hahaha");
}


注意:这里没有设置length,这个函数必然会导致一些潜在的问题。而这种情况在设计大项目情况下很可能出现,因此每个函数必须有责任保证自己操作过后对象是完整的,而setString恰恰不能保证完整性。
引用 22 楼 hahaysw 的回复:
回19楼,这是受了书上例子的影响。 
你有更好的实现方法吗?不建立setString, 
让我学习学习,非常感谢~

#25


String s1="abcd"; //调用String(const char*)构造
String s2=s1;    //调用String(const String&)构造
String s3;
s3 = s1; //调用operator=

#26


回23楼,改了后是可以了,但是还是有些小问题。
就是<<s3输出的是happy birthday而不是 to you。而且我加了书上的复制构造函数也是出可以的结果。

回24楼,现在清楚了,以后会养成这样的习惯的。
再次麻烦你帮看看,为什么输出s3时是happy birthday而不是 to you?要怎么改?

#27


按照下面顺序理论上应该先出现
s1 is happy 
s2 is birthday
s3 is to you
s1+s2 is happybirthday
s1+s2+s3 is happybirthdayto you
s3=s1+s2 is happybirthday

如果不是这样,估计这和ios的缓存有关,建议你把所有的\n都换成endl,强迫ios刷新缓存
cout  <  <"s1 is "  <  <s1  
<  <"\ns2 is "  <  <s2  
<  <"\ns3 is "  <  <s3//还没到s3=s1+s2就帮我提前修改了s3,想不通为什么。  
<  <"\ns1+s2 is "  <  <s1+s2  
<  <"\ns1+s2+s3 is "  <  <s1+s2+s3  
<  <"\ns3=s1+s2 is "  <  <(s3=s1+s2)  <  <endl;  

#28


非常非常感谢啊~
的确行了,看来endl才是万能的。
啊哈哈哈~~~

#29


它也就多调用一次flush而已

#30


今天一定能睡个好觉!

#31


该回复于2008-04-20 20:16:18被版主删除

#32


mark

#33


mark2 

#34


好复杂。。。。。

#35


确实
 ,有点复杂

#36


LZ的代码看上去有点乱,密密麻麻的....

#37


有点乱

#1


String operator+(const String &);  
是这样写的.

#2


一般来说, +重载为友元更好. 
friend String operator+(const String &str1, const String &str2) {
    char *result = new char[strlen(str1.size()) + strlen(str2.size()) + 1];
    strcpy(result, str1.pstr);
    strcat(result, str2.pstr);

    String rs(result);
    delete result;

    return rs;
}

#3


String String::operator +(const String &right1, const String &right2)
+是双目运算

#4


String String::operator +(const String &right)  
{  
char *tempptr;       
length+=right.length;  
tempptr=new char[length+1];  
        strcpy(tempptr,sptr);     
strcat(tempptr,right.sptr);  
cout  <  <" b\n";  
return tempptr;  
}
不能返回局部变量的指针,局部变量在函数的尾部就被删除了,返回局部变量的指针是没有意义的。

#5


回2楼,我按照你说的改了。程序可以运行。但是达不到想要的效果。
回3楼,厄~我看书上说对于双目运算符有2种重载:
1是只有1个参数的类成员函数。试用于2个对象都是类对象。
2是有2个参数。试用于有1个对象不是类对象。就像重载<<.
如果String String::operator +(const String &right1, const String &right2)行
麻烦你可以发发代码怎么实现吗?
我想学习学习,顺便解决我的问题。


最后,希望各位高手帮帮忙~

#6


+是双目运算,但成员函数隐式传递this指针,所以可以这样定义:
String operator+(const String &);   

#7


好久没看C++代码,现在一看真是有点熟悉而又一些陌生。

#8


回4楼,我把char *tempptr声明为private了,还是不行,而且跟原来的输出没什么区别。
可以说清楚点不?

#9


s1+s2要产生一个临时对象,所以也要调用构造函数,当输出s1+s2后,就调用析构函数删除临时对象
s1+s2+s3要产生两个临时对象,一个是s1+s2,一个是s1+s2+s3。
//string2.h
#ifndef STRING2_H  
#define STRING2_H  
#include  <iostream.h>  
class String{  
friend ostream &operator  <<(ostream &,const String &);  
public:  
String(const char * ="" );  
~String();  
String operator+(const String &);  
const String &operator=(const String &);  
private:  
int length;  
char *sptr;  
void setString(const char *);
char *tempptr;  
};  
#endif 
#include  <iostream.h>  
#include  <iomanip.h>  
#include  <string.h>  
#include  <assert.h>  
#include  "string2.h"  
String::String(const char *s):length(strlen(s))  
{  
cout  <<"conversion constructor: "  <<s  <<" a\n";  
setString(s);  
}  
String::~String()  
{  
cout  <<"destructor: "  <<sptr  <<'\n';  
delete [] sptr;  
}  
void String::setString(const char *string2)  
{  
sptr=new char[length+1];  
assert(sptr!=0);  
strcpy(sptr,string2);  
}  
String String::operator +(const String &right)  
{  
       
length+=right.length;  
tempptr=new char[length+1];  
        strcpy(tempptr,sptr);     
strcat(tempptr,right.sptr);  
cout  <<" b\n";  
return tempptr;  
}  
const String &String::operator =(const String &right)  
{  
cout  <<"operator=called\n";  
if(&right!=this){  
delete [] sptr;  
length=right.length;  
setString(right.sptr);  
}  
else  
cout  <<"attempted assingnement of a string to itself\n";  
return *this;  
}  
ostream &operator  <<(ostream &output,const String &s)  
{  
output  <<s.sptr;  
return output;  
}   
int main()  
{  
String s1("happy"),s2(" birthday"),s3(" to you.");  
//String s=s1+s2; 
cout  <<"s1 is "<<s1<<endl;  
cout<<"\ns2 is "<<s2<<endl;  
cout<<"\ns3 is "<<s3<<endl;//还没到s3=s1+s2就帮我提前修改了s3,想不通为什么。  
cout<<"\ns1 add s2 is "<<s1+s2<<endl;  
cout<<"\ns1 add s2 add s3 is "<<s1+s2+s3<<endl;  
cout<<"\ns3=s1+s2 is "<<(s3=s1+s2)<<endl;  
return 0;  

输出结果为:
conversion constructor: happy a
conversion constructor:  birthday a
conversion constructor:  to you. a
s1 is happy

s2 is  birthday

s3 is  to you.
 b
conversion constructor: happy birthday a

s1 add s2 is happy birthday
destructor: happy birthday
 b
conversion constructor: happy birthday a
 b
conversion constructor: happy birthday to you. a

s1 add s2 add s3 is happy birthday to you.
destructor: happy birthday to you.
destructor: happy birthday
 b
conversion constructor: happy birthday a
operator=called

s3=s1+s2 is happy birthday
destructor: happy birthday
destructor: happy birthday
destructor:  birthday
destructor: happy
Press any key to continue

#10


String String::operator +(const String &right)  
{  
static char *tempptr;       
length+=right.length;  
tempptr=new char[length+1];  
        strcpy(tempptr,sptr);     
strcat(tempptr,right.sptr);  
cout   <   <" b\n";  
return tempptr;  

这样试试~

#11


正确的应该是
String operator+(const String &)const;   
引用 1 楼 akirya 的回复:
String operator+(const String &);   
是这样写的.

#12


这种东西没有任何理由重载为友元,为什么它是友元更好?
一般来说,友元只有在第一个参数不是自己类型时才用如
friend String operator+(LPCTSTR str1, const String& str2);
对于第一个参数是同一个类的引用时,友元没有任何好处
引用 2 楼 Inhibitory 的回复:
一般来说, +重载为友元更好.  
friend String operator+(const String &str1, const String &str2) { 
    char *result = new char[strlen(str1.size()) + strlen(str2.size()) + 1]; 
    strcpy(result, str1.pstr); 
    strcat(result, str2.pstr); 

    String rs(result); 
    delete result; 

    return rs; 
}

#13


不知道你怎么会达不到效果,下面代码很容易做到

String String::operator+(const String & str)const
{
      String res;
      res.length = length + str.length;
      res.sptr = new char[res.length+1];
      strcpy(res.sptr,sptr);
      strcat(res.sptr,str.sptr);
      return res;     
}

#14


这个不是返回局部变量指针,只要它有正确的拷贝构造函数和operator=即可
因为这个函数返回的对象必然是临时的,因此接收此返回值的表达式必须有clone这个对象的能力,这就必须实现拷贝构造和operator=
引用 4 楼 coverallwangp 的回复:
String String::operator +(const String &right)   
{   
char *tempptr;        
length+=right.length;   
tempptr=new char[length+1];   
        strcpy(tempptr,sptr);      
strcat(tempptr,right.sptr);   
cout   <   <" b\n";   
return tempptr;   

不能返回局部变量的指针,局部变量在函数的尾部就被删除了,返回局部变量的指针是没有意义的。 

#15


String operator+(const String &)const; 
后面的const是使this的指针变为const
调用该函数的对象是只读的,不能改变它.
所以想调用s1+s2不变,应该在后面加const

#16


回13楼,真的不行。弹出一个对话框,有 终止 重试 取消 之类的。就想运行非法一样。

#17


下面这个函数是很不对的

void String::setString(const char *string2)  
{  
sptr=new char[length+1];  
assert(sptr!=0);  
strcpy(sptr,string2);  
}  


这个函数总是假定length已经提前设置好了,这个把一个类的各个属性分开设置的方法违背了“consistency”的原则
应该这样设计才对


void String::setString(const char *string2)  
{  
    length = strlen(string2);
    sptr=new char[length+1];  
    strcpy(sptr,string2);  
}  

#18


那不是这个函数的原因,而是你没有实现拷贝构造
考虑这样的一个式子
String s1("abc");
String s2("def");
String s3 = s1+s2;
由于s1+s2返回一个String对象(注意它是临时对象),当S3接收这个值时,由于你没有实现拷贝构造,系统会非常傻的替你实现一个逐字节拷贝的拷贝构造函数

假定临时对象是_s3,其sptr指向某个new出来的地址,拷贝后,_s3和s3的sptr都指向它

当_s3和s3分别析构,问题就出来了。由于s3, _s3分别通过~String调用delete[]sptr,这必然delete同一个指针两次,从而导致问题。

引用 16 楼 hahaysw 的回复:
回13楼,真的不行。弹出一个对话框,有 终止 重试 取消 之类的。就想运行非法一样。

#19


搞不懂你干吗要单独实现一个setString,而又不把length的赋值操作放在里面干吗?

#20


mark 一个

#21


把String String::operator+(const String & str)const
{
      String res;
      res.length = length + str.length;
      res.sptr = new char[res.length+1];
      strcpy(res.sptr,sptr);
      strcat(res.sptr,str.sptr);
      return res;     
}加进来的确可以,不过要加如下面的构造函数
String::String(const String &copy):length(copy.length)
{                                                   
cout<<"copy constructor: "<<copy.sptr<<'\n';   
setString(copy.sptr);
}
书上说这是一个复制构造函数,它通过复制已存在的String对象来初始化另一对象。

其实我对这2个构造函数也不是完全理解:
String::String(const char *s):length(strlen(s))
说可以把char *转换为类对象。
String::String(const String &copy):length(copy.length)
说通过复制已存在的String对象来初始化另一对象。

有高手可以说下怎么理解不?要不明天我另外开贴~

#22


回19楼,这是受了书上例子的影响。
你有更好的实现方法吗?不建立setString,
让我学习学习,非常感谢~

#23


楼主,我先回答你第一个问题:

cout  <<"s1 is "  <<s1  
<<"\ns2 is "  <<s2  
<<"\ns3 is "  <<s3//还没到s3=s1+s2就帮我提前修改了s3,想不通为什么。  
<<"\ns1+s2 is "  <<s1+s2  
<<"\ns1+s2+s3 is "  <<s1+s2+s3  
<<"\ns3=s1+s2 is "  <<(s3=s1+s2)  <<endl;  

以上原因是因为流是从右向左逐一运算的,所以当你显示s3的时候已经运行完毕s3=s1+s2这个操作了.
加号运算符
另外如何重载运算符,一定要服从著名的BIG THREE原则,即:需要定义析构函数,复制构造函数和复制运算符中任意一个的时候,一定要定义另外两个,你程序中的

<<"\ns1+s2 is "  <<s1+s2  //这部分需要复制构造函数


所以说+运算符重载,也需要考虑到其他问题.
我改好的代码如下:

//string2.h  
 
#include  <iostream.h>  
class String{  
friend ostream &operator  <<(ostream &,const String &);  
public:  
String(const char * ="" );  
String(const String & tempStr);
~String();  
    String operator+(const String &);  
const String &operator=(const String &);  
private:  
int length;  
char *sptr;  
void setString(const char *);  
};  

//string2.cpp  
#include  <iostream.h>  
#include  <iomanip.h>  
#include  <string.h>  
#include  <assert.h>  
#include"string2.h"  
String::String(const char *s):length(strlen(s))  
{  
cout  <<"conversion constructor: "  <<s  <<" a\n";  
setString(s);  
}  
String::~String()  
{  
cout  <<"destructor: "  <<sptr  <<'\n';  
delete [] sptr;  
}  
void String::setString(const char *string2)  
{  
sptr=new char[strlen(string2)+1];  
assert(sptr!=0);  
strcpy(sptr,string2);
}  
String String::operator +(const String &right)  
{  
char *tempptr=new char[length+right.length+1];  
        strcpy(tempptr,sptr);     
strcat(tempptr,right.sptr);  
cout  <<" b\n"; 
String tempStr(tempptr); 
return tempStr;
}  
const String &String::operator =(const String &right)  
{  
cout  <<"operator=called\n";  
if(&right!=this){  
delete [] sptr;  
length=right.length;  
setString(right.sptr);  
}  
else  
cout  <<"attempted assingnement of a string to itself\n";  
return *this;  
}  

ostream&  operator <<(ostream &output,const String &s)  
{  
output  << s.sptr;  
return output;  
}  

String::String(const String & tempStr)
{
length=tempStr.length;
setString(tempStr.sptr);
}

//main().cpp  
#include  <iostream.h>  
#include"string2.h"  
int main()  
{  
String s1("happy"),s2(" birthday"),s3(" to you.");  

cout  <<"s1 is "  <<s1<<endl  ;
cout<<"\ns2 is "  <<s2 <<endl ;
cout<<"\ns3 is "  <<s3<<endl;
cout<<"\ns1+s2 is "  <<s1+s2 <<endl ;
cout<<"\ns1+s2+s3 is "  <<s1+s2+s3<<endl  ;
cout<<"\ns3=s1+s2 is "  <<(s3=s1+s2)  <<endl;  
return 0;  
}  

#24


写一个私有的setString也不是不可以,但是你需要把length的赋值也放到里面去,否则可能会造成对象自身的不一致
假如你写了这个类2年之后,你又加了一个函数如下:


void String::foo()
{
    setString("hahaha");
}


注意:这里没有设置length,这个函数必然会导致一些潜在的问题。而这种情况在设计大项目情况下很可能出现,因此每个函数必须有责任保证自己操作过后对象是完整的,而setString恰恰不能保证完整性。
引用 22 楼 hahaysw 的回复:
回19楼,这是受了书上例子的影响。 
你有更好的实现方法吗?不建立setString, 
让我学习学习,非常感谢~

#25


String s1="abcd"; //调用String(const char*)构造
String s2=s1;    //调用String(const String&)构造
String s3;
s3 = s1; //调用operator=

#26


回23楼,改了后是可以了,但是还是有些小问题。
就是<<s3输出的是happy birthday而不是 to you。而且我加了书上的复制构造函数也是出可以的结果。

回24楼,现在清楚了,以后会养成这样的习惯的。
再次麻烦你帮看看,为什么输出s3时是happy birthday而不是 to you?要怎么改?

#27


按照下面顺序理论上应该先出现
s1 is happy 
s2 is birthday
s3 is to you
s1+s2 is happybirthday
s1+s2+s3 is happybirthdayto you
s3=s1+s2 is happybirthday

如果不是这样,估计这和ios的缓存有关,建议你把所有的\n都换成endl,强迫ios刷新缓存
cout  <  <"s1 is "  <  <s1  
<  <"\ns2 is "  <  <s2  
<  <"\ns3 is "  <  <s3//还没到s3=s1+s2就帮我提前修改了s3,想不通为什么。  
<  <"\ns1+s2 is "  <  <s1+s2  
<  <"\ns1+s2+s3 is "  <  <s1+s2+s3  
<  <"\ns3=s1+s2 is "  <  <(s3=s1+s2)  <  <endl;  

#28


非常非常感谢啊~
的确行了,看来endl才是万能的。
啊哈哈哈~~~

#29


它也就多调用一次flush而已

#30


今天一定能睡个好觉!

#31


该回复于2008-04-20 20:16:18被版主删除

#32


mark

#33


mark2 

#34


好复杂。。。。。

#35


确实
 ,有点复杂

#36


LZ的代码看上去有点乱,密密麻麻的....

#37


有点乱