这是一个很有趣的练习。它让我了解了标准库String类的实现,并且增强了我对面向对象编程的理解。这个String类的对象基于采用引用计数的String类表示(Srep),多个String之间将会共享同一个表示。并且在写时仍然采用值语义(即,对一个String的操作不会影响到另一个String),这使用了一种叫“写时复制”的计数,只在对String类进行修改时再复制String类对象。这个良好的策略将会使得用户即可以使用值语义的String字符串又可以不用负担过大的内存开销(因为保存同一内容的String会共享同一表示)。
还有一个很关键的部分是Cref这个成员类。这个类的作用相当于一个char&,在调用String的下标操作符时返回。然而,为什么不直接返回一个char&呢?因为如果直接返回char&的话就允许了用户对String的随意修改,而这与String的设计意图是矛盾的。String的设计意图是在修改String时对String进行复制(与原来共享的表示分离)。也就是说,我们并不想返回一个低级表示给用户随意使用。在这种情况下我们定义了一个Cref类。这个类将在用户读取String的[]下标内容时转换为一个char,而在用户修改这个字符(例如,String[0]='a')时复制String的表示(通过定义赋值操作符)。
这些练习中最困难的是设计String类使得子串可以用在赋值的左边。我仔细想了想这里也应该运用相同的策略:返回一个Sref类型保存绑定到String的子串的引用。在读取时返回一个String,在修改时对原String进行复制。
我还定义了一个find(const String&)函数。注意到我在find函数中并没有进行逐个的char比较,而是调用了operator==比较参数和目标String的子串。这也是C++程序设计语言一直强调的,尽量少操作对象的直接表示。
输入操作符operator>>我的实现很简陋,创建一个大小为20的char数组保存输入。我无法想出一个更好的实现。如果大家有更好的实现方法,希望能留言建议,谢谢!
String.h
//2.(*2) 完成并测试String类。(完成拼接运算符和IO操作符)
//7.(*2) 为String 类定义一个外部迭代器String_tier。添加一些操作。
//8.(*1.5) 通过重载()为String类提供一个字串操作。
//9.(×3) 设计String类使得字串可以用在赋值的左边。
//写一个版本允许给串赋的字串长度变化。
//10.(*2) 为String定义一个操作,产生其值的C字串表示。
//11.(*2.5) 为String定义和实现一个简单的模式匹配功能函数。
//(我实现的是一个find函数。)
#ifndef STRING_H
#define STRING_H
#include <iostream>
#include <cstring>
class String{
struct Srep{
char* s;
int sz;
int n;
Srep(int nsz,const char* p):
sz(nsz),n(1),s(new char[sz+1]){strcpy(s,p);}
~Srep(){delete[] s;}
Srep* get_own_copy()
{
if(n==1) return this;
--n;
return new Srep(sz,s);
}
void assign(int nsz,const char* p)
{
if(sz!=nsz){
delete[] s;
sz=nsz;
s=new char[sz+1];
}
strcpy(s,p);
}
private:
Srep(const Srep&);
Srep& operator=(const Srep&);
};
Srep* rep;
public:
friend class String_iter;
class Cref{
friend class String;
String& s;
int i;
Cref(String& ss,int ii):s(ss),i(ii){}
public:
operator char() const{return s.read(i);}
void operator=(char c){s.write(i,c);}
};
class Sref{
friend class String;
String& s;
int nbeg,nend;
Sref(String& ss,int b,int e):s(ss),nbeg(b),nend(e){}
public:
operator String() const
{return static_cast<const String&>(s)(nbeg,nend);}
void operator=(const String& ss)
{
if(nend-nbeg==ss.size()-1){
s.rep=s.rep->get_own_copy();
for(int ix=nbeg;ix<=nend;++ix)
s.rep->s[ix]=ss.rep->s[ix];
}else{
//这里的方法未必要一样。只要可行即可。 :
String left=
nbeg==0?"":static_cast<const String&>(s)(0,nbeg-1);
String right=
nend==s.rep->sz-1?"":static_cast<const String&>(s)(nend+1,s.rep->sz-1);
s=String();
((const_cast<String&>(s)+=left)+=ss)+=right;
}
}
};
class Range{};
String():rep(new Srep(0,"")){}
String(const char* s):rep(new Srep(strlen(s),s)){}
String(const String& x)
{
++x.rep->n;
rep=x.rep;
}
String& operator=(const char*);
String& operator=(const String&);
String& operator+=(const char* s)
{
rep=rep->get_own_copy();
strcat(rep->s,s);
rep->sz+=strlen(s);
return *this;
}
String& operator+=(const String& x)
{
rep=rep->get_own_copy();
strcat(rep->s,x.rep->s);
rep->sz+=x.rep->sz;
return *this;
}
friend std::ostream& operator<<(std::ostream& os,const String& x)
{return os<<x.rep->s;}
friend std::istream& operator>>(std::istream&,String&);
friend bool operator==(const String& x,const char* s)
{return strcmp(x.rep->s,s)==0;}
friend bool operator==(const String& x,const String& y)
{return strcmp(x.rep->s,y.rep->s)==0;}
friend bool operator!=(const String& x,const char* s)
{return strcmp(x.rep->s,s)!=0;}
friend bool operator!=(const String& x,const String& y)
{return strcmp(x.rep->s,y.rep->s)!=0;}
~String(){if(--rep->n==0) delete rep;}
void check(int i) const{if(i<0||rep->sz<=i) throw Range();}
char read(int i) const{return rep->s[i];}
void write(int i,char c){rep=rep->get_own_copy();rep->s[i]=c;}
int find(const String&) const;
char operator[](int i) const{check(i);return rep->s[i];}
Cref operator[](int i){check(i);return Cref(*this,i);}
String operator()(int,int) const;
Sref operator()(int,int);
const char* c_str() const{return rep->s;}
int size() const{return rep->sz;}
};
String& String::operator=(const char* s)
{
if(rep->n==1)
rep->assign(strlen(s),s);
else{
--rep->n;
rep=new Srep(strlen(s),s);
}
return *this;
}
String& String::operator=(const String& x)
{
++x.rep->n;
if(--rep->n==0) delete rep;
rep=x.rep;
return *this;
}
String String::operator()(int nbeg,int nend) const
{
check(nbeg);check(nend);
if(nbeg<=nend){
char *ca=new char[nend-nbeg+2];
strncpy(ca,rep->s+nbeg,nend-nbeg+1);
ca[nend-nbeg+1]='\0';
String ret(ca);
delete[] ca;
return ret;
}else
return String("");
}
String::Sref String::operator()(int nbeg,int nend)
{
check(nbeg);check(nend);
if(nbeg<=nend){
return Sref(*this,nbeg,nend);
}else{
String tp("");
String& temp=tp;
return Sref(temp,0,0);
}
}
int String::find(const String& x) const
{
int pos=0;
int sz=x.size();
while(pos+sz<=this->size()&&x!=this->operator()(pos,pos+sz-1))
++pos;
if(pos+sz<=this->size())
return pos;
else
return -1;
}
String operator+(const String& x,const char* s)
{
String ret(x);
ret+=s;
return ret;
}
String operator+(const String& x,const String& y)
{
String ret(x);
ret+=y;
return ret;
}
std::istream& operator>>(std::istream& is,String& x)
{
char* s=new char[20];
is>>s;
x=s;
return is;
}
class String_iter{
mutable int ix;
String& str;
public:
String_iter(String& s):str(s),ix(0){}
char& next()
{
str.rep=str.rep->get_own_copy();
return str.rep->s[ix++];
}
char next() const{return str.rep->s[ix++];}
String_iter& operator++(){++ix;return *this;}
String_iter operator++(int);
String_iter& operator--(){--ix;return *this;}
String_iter operator--(int);
};
String_iter String_iter::operator++(int)
{
String_iter ret(*this);
++*this;
return ret;
}
String_iter String_iter::operator--(int)
{
String_iter ret(*this);
++*this;
return ret;
}
#endif
main.cpp
#include <iostream>
#include "String.h"
using namespace std;
int main()
{
String s("Hello C++");
cout<<s<<endl;
cout<<s.find("C++")<<endl;
s(6,8)="World";
cout<<s<<endl;
cout<<s(0,4)<<endl;
return 0;
}
大家有什么不懂的可以问。乐意解答。