VC驿站__编写一个精简版的string类
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
编写一个精简版的string类
该类的具体功能如下所示.
1 计算字符串的长度,类似于string类的size或者length
2 可对字符串进行赋值,如String str="hello world";
3 可将char型字符串直接赋给String型字符串,如:
String str="Hello world";
char ch[10]="not at all";
str=ch;
4 可如同对象那样将字符串进行初始化,String str("Mother");
5 可将两个字符串相加,然后将合并后的字符串赋给另一个字符串,如String str=s2+s3;
6 可执行加等操作(+=),也就是将第1个字符串与第2个字符串相加,然后再赋给第1个字符串,如:String s1+=s2
7 可对两个字符串进行比较,如str1==str2,str1<str2或者str1>str2.
8 可根据字符串的大小自动调节保存字符串的数组的大小。
9 限制数组越界
10通过成员函数输出字符串,如s1.getlen()。
11 通过重载输出运算符<<输出字符串,如:
string s1("hello"),s2("good");
cout<<s1<<s2;
12 通过重载输入运算符>>输入字符串,如:cin>>s1>>s2
创建String类
上节简单地对精简版的String类的功能进行了介绍,本节首先实现这个String类。
我们知道C++自带的String类在声明一个字符串后,如:
String s;
该字符串s仅仅是声明,并未初始化,因此该字符串是个空字符,只保存了结束符号"\0"。
我们首先来实现这个功能。
由于对象的创建是通过构造函数来实现的,因此我们可以通过默认构造函数来构造一个空字符串。
通过String类的默认构造函数实现空字符串。
程序代码如下:
1.#include <iostream>
ing namespace std;
3.class String
4.{
5.public:
6. String();
7. int getlen()const{return len;}
8.private:
9. int len;
10. char *str;
11.};
12.String::String()
13.{
14. len = 0;
15. str=new char[1];
16. str[0]='\0';
17.}
18.int _tmain(int argc, _TCHAR* argv[])
19.{
20. String s1;
21. cout<<s1.getlen();
22. system("pause");
23. return 0;
24.}
输出:0
分析:
第6行声明了一个默认构造函数,第12~17行是该函数的定义部分。
第14行将所创建的字符串的长度len设置为0,len在第9行定义。
第15行在堆中创建一个char型数组,该数组只有一个元素,str保存该数组的地址,char 型指针str在第10行定义。
第16行将char型数组str中唯一一个元素保存结束字符"\0",因此该char型数组变成了一
个字符串,一个长度为0的空字符串。
main函数开始于第19行,第20行用我们创建的String类声明了一个字符串s1,然后第21行输出该字符串的长度,为0.成员函数getlen()在第7行定义,它的作用是返回字符串长度len的值。
创建可以自动调节大小的String类字符串对象
本节我们将在已创建空字符串的基础上创建一个有字符的字符串,并且该字符串课根据字符的长度自动调节数组的大小。
要实现这个功能,我们需要一个带参数的构造函数,如:
1.String::String(const char*const ch)
2.{
3. len = strlen(ch);
4. str = new char[len+1];
5. for(int i=0;i<len;i++)
6. str[i] = ch[i];
7. str[len]='\0';
8.}
复制代码
这样,假如我们定义并初始化了一个字符串,如String s("Hello world"); 就会自动调用这个带一个参数的构造函数,同时将"hello world"传递到函数中,ch接收了这个字符串,第3用strlen计算"hello world"的长度,也就是ch的长度,该长度同时赋给String 类的私有成员变量len。
第4行在堆中创建了一个char型数组,它的长度是len+1,最后一个字符用来保存结束符"\0"。
该char型数组的存地址赋给String类的私有指针成员变量str。
第5~6行是一个for循环,从字符串ch第1个字符开始,也就是"hello world",每循环一次,即将ch中的一个字符赋给str指向的在堆中创建的char型数组,一直到结束字符前的最后一个字符为止。
最后一行用str指向的在堆中创建的char型数组中的最后一个元素保存结束符号"\0"。
这样,我们就可以创建一个恰好能容纳所有字符的char型字符串数组。
创建可自动调节大小的String类字符串对象。
程序代码如下:
1.#include <iostream>
ing namespace std;
3.class String
4.{
5.public:
6. String();
7. String(const char*const ch);
8. int getlen()const{return len;}
9. const char*getstr()const{return str;}
10.private:
11. int len;
12. char *str;
13.};
14.String::String()
15.{
16. cout<<"默认构造函数\n";
17. len = 0;
18. str=new char[1];
19. str[0]='\0';
20.}
21.String::String(const char*const ch)
22.{
23. cout<<"构造函数\n";
24. len = strlen(ch);
25. str = new char[len+1];
26. for(int i=0;i<len;i++)
27. str[i] = ch[i];
28. str[len]='\0';
29.}
30.int _tmain(int argc, _TCHAR* argv[])
31.{
32. String s1;
33. cout<<"s1:"<<s1.getstr()<<"\t共"<<s1.getlen()<<"字符
"<<endl;
34. String s2("study");
35. cout<<"s2:"<<s2.getstr()<<"\t共"<<s2.getlen()<<"字符
"<<endl;
36. char *temp = "help me";
37. String s3(temp);
38. cout<<"s3:"<<s3.getstr()<<"\t共"<<s3.getlen()<<"字符
"<<endl;
39. system("pause");
40. return 0;
41.}
复制代码
分析:
第7行定义了带一个参数的构造函数用来初始化字符串,第22行为该函数的定义部分。
由于程序中是初始化不是赋值,我们在初始化过程中不会改变字符串的值,因此将ch定义为
const。
要注意,参数是个指向char型数组的指针,它的作用类似于数组名,保存的都是数组第1个元素的地址。
第35行在创建s2对象时调用了这个构造函数,并将未命名的字符串"study"作为参数传递到该构造函数中,由于未命名的字符串"study"其实是第1个字符串的存地址,因此接受该字符串的指针ch指向的就是该字符串的第1个字符,这样我们在函数中就可通过下标来处理ch,也就是处理字符串"study"。
我们首先用strlen函数计算字符串ch的长度,然后创建一个用样长度+1的字符数组,该数组最后一位保存字符串结束标志"\0"接着以下标的方式将"study"的每个字符赋给新建的长度+1的字符数组,最后将字符串结束标志"\0"赋给新建的字符数组的最后一个元素,完成字符串的创建工作。
第36行通过成员函数getstr()输出了这个字符串,成员函数getstr()在第9行定义,它的作用是返回私有指针成员str指向的char型数组。
该数组在构造函数中被创建,并且保存的是初始化的字符串,因此,getstr()就是输出某个String对象的char型数组成员,或者说字符串成员。
限制数组越界
本节我们在给这个String类添加一个新功能——限制数组越界,如:
1.char &String::operator [](int length)
2.{
3. if(length>len)
4. return str[len-1];
5. else
6. return str[length]
7.}
复制代码
第1行重载了属于String类的下标运算符函数operator[],该函数有一个整型参数,即数组的下标length,该下标值会传递到函数体中进行判断,假如下标超过了数组长度,那么返回数组长度-1,也就是最后一个可见字符,假如该下标长度不超过数组长度,那么返回该下标的数组元素。
这样,就算用户超出数组长度地输入或者输出一个字符,如:cout<<s[999],或cin>>s[999],由于超过数组长度返回的是最后一个字符,因此不会出现在超出数组末尾修改数据的现象,这无形中就提高了代码的安全性。
增加限制数组越界功能的String类的例子如下所示。
限制数组越界。
程序代码如下:
1.#include <iostream>
ing namespace std;
3.class String
4.{
5.public:
6. String();
7. String(const char *const ch);
8. char&operator[](int length); //按引用返回的重载下标运算
符函数
9. int getlen()const{return len;}
10. const char *getstr()const{return str;}
11.private:
12. int len;
13. char *str;
14.};
15.String::String()
16.{
17. len = 0;
18. str = new char[1];
19. str[0]='\0';
20.}
21.String::String(const char *const ch)
22.{
23. len = strlen(ch);
24. str = new char[len+1];
25. for(int i=0;i<len;i++)
26. str[i] = ch[i];
27. str[len]='\0';
28.}
29.char &String::operator [](int length)
30.{
31. if(length>len)
32. return str[len-1];
33. else
34. return str[length];
35.}
36.int _tmain(int argc, _TCHAR* argv[])
37.{
38. String s1;
39. cout<<"s1:"<<s1.getstr()<<"\t 共"<<s1.getlen()<<"字符
"<<endl;
40. String s2("study");
41. cout<<"s2:"<<s2.getstr()<<"\t 共"<<s2.getlen()<<"字符
"<<endl;
42. char *temp = "help me";
43. String s3(temp);
44. cout<<"s3:"<<s3.getstr()<<"\t 共"<<s3.getlen()<<"字符
"<<endl;
45. cin>>s3[999];
46. cout<<"s3:"<<s3.getstr()<<"\t 共"<<s3.getlen()<<"字符
"<<endl;
47. cout<<"s3[999]:"<<s3[999]<<endl;
48. system("pause");
49. return 0;
50.}
分析:
第8行声明了重载下标运算符函数operator[],该函数按别名方式返回char型字符,第
32~38行是该函数的定义部分。
第48行调用operator[]函数,下标值999被传递到operator[]函数中,由于该下标值大于数组长度len,因此返回len-1个数组元素,也就是最后一个可见字符。
cin流对象将用户输入的第1个字符保存在该数组最后一个可见字符的位置处。
第49行首先输出该数组的所有元素,即所有可见字符,我们看到最后一个字符由原来的e 变成了z。
第50行又试图超出数组末尾输出字符,结果这一错误被operator[]函数捕获,因此返回的仍然是数组的最后一个元素,即字符z。
用复制构造函数实现字符串的赋值功能
本节我们再为这个String类添加赋值功能,该功能可以再构造对象的时候进行赋值,如:string s4=s3;
我们知道,在构造一个对象时用另一个对象来初始化该对象的数据成员,需要调用一个函数,该函数就是复制构造函数,默认的复制构造函数都是浅层复制,由于我们需要在两个对象间复制的成员是个指针,因此必须重写复制构造函数,重写的复制构造函数实现对象之间的深层复制的过程如下:
1.String::String(const String &rs)
2.{
3. len = rs.getlen();
4. str = new char[len+1];
5. for(int i=0;i<len;i++)
6. str[i] = rs[i];
7. str[i]='\0';
8.}
复制代码
第1行定义了一个String类的复制构造函数,该函数按别名方式接受另一个对象,然后在函数体的第1行首先获取该对象的字符串成员的长度,该值返回给len,然后在堆中开辟一块新空间,该空间用来保存char型数组,这个char型数组的长度与传递到复制构造函数中的源对象相同,len是可见字符长度,再加1用于保存结尾空字符,最后返回该空间的地址给待复制的目标对象的指针成员str。
第3行和第4行跟着一个循环将旧对象的字符串成员复制到新对象的字符串成员中,然后用空字符结束新字符串。
由于我们重载了两个带一个参数的构造函数,第1个是按指针方式传递char型数组的构造函数,第2个是按别名方式传递String类对象的复制构造函数,这两个构造函数中都使用了下标运算符,因此我们需要在重载一个下标运算符函数operator[],该函数与复制构造函数搭配使用,因此该函数要在函数体前面加上const,以便于复制构造函数的const对象调用,如:
1.char String::operator[](int length)const
2.{
3. if(length>len)
4. return str[len-1];
5. else
6. return str[length];
7.}
复制代码
由于我们是在复制构造函数中调用该函数,并且临时对象在复制构造函数结束后即被销毁,因此没必要按地址返回临时对象的数据成员,这里的operator[]重载下标运算符函数按值返回char型字符,同时在函数体前面加了const修饰符,表示该函数可以操作const对象。
这样,两个同名函数的类型不同,就可以做到对该函数的重载。
const修饰符实现重载。
程序代码如下:
1.#include <iostream>
ing namespace std;
3.class A
4.{
5.public:
6. A(int i,int j){x=i;y=j;}
7. void set(int s1,int s2){x=s1,y=s2;}
8. void print(){cout<<x<<endl;}
9. void print()const{cout<<y<<endl;}
10.private:
11. int x,y;
12.};
13.int _tmain(int argc, _TCHAR* argv[])
14.{
15. const A a(2,3);
16. a.print();
17.// a.set(4,5);
18. A b(2,3);
19. b.print();
20. b.set(4,5);
21. b.print();
22. system("pause");
23. return 0;
24.}
复制代码
输出:
3
2
4
分析:
在该程序中重载了两个print()函数,第1个未用const修饰,功能是输出成员变量x的值,见第8行,第2个用const修饰了函数体,功能是输出成员变量y的值,见第9行。
第15行定义了一个const对象a,并且初始化该对象的成员变量x和y为2和3.
第16行调用print()函数,我们发现输出的是3,也就是y的值,这证明我们调用的是带const修饰符的print()成员函数。
第18行定义了一个非const对象b,初始化的值仍是2和3,然而第19行调用print()函数后输出却是2.这说明调用的是非const成员函数print()。
第20行调用set()函数修改了非const对象b的x和y数据成员,为4和5,第21行调用非const成员函数print()输出了4.
第17行被注释起来,这是因为被修饰为const对象的成员值不能被修改,而第17行试图修改const对象a中的成员x和y的值,这导致编译出错,因此将该行注释了起来。
另外,假如我们将第9行的const成员函数print()注释起来,那么编译也会出错,解决的办法是将第16行注释起来,也就是不用const对象调用const成员函数print()。
这说明const对象只能调用const成员函数,并且const对象的值不能被修改。
注意:
const对象只能调用const成员函数,并且const对象的值不能被修改。
只有const成员函数才有资格操作常量或const对象,没有使用const关键字说明的成员函数不能用来操作const对象。
const不光可以定义常量,而且还可用来修饰函数的参数,返回值,甚至函数的定义体。
const 修饰函数参数,表示函数参数不可更改,const修饰返回值,表示返回值不可改变,const 修饰函数的定义体,表示在函数体不能修改对象的任意成员,在const成员函数体试图修改const对象的数据成员的值将会产生语法错误,而且在const函数体中调用非const成员函数也会产生语法错误。
基类的成员函数可以被派生类继承并覆盖,那么假如我们将基类的成员函数体前面加修饰符const的话,可防止覆盖函数改变数据成员的值。
跳回到重载下标运算符函数operator[]中,如下:
1.char String::operator[](int length)const
2.{
3. if(length>len)
4. return str[len-1];
5. else
6. return str[length];
7.}
复制代码
函数体中对下标值进行判断,假如下标值超过数组字符长度,那么返回该数组最后一个可见字符,否则的话,返回处于该下标位置的数组元素。
我们看到,由于该函数体中只是返回this对象的某个数组元素,并不对该this对象的某个成员进行修改,因此这里在函数体前面用了const修饰符,这样可避免在该函数中发生试图修改当前对象的某个成员的错误现象,同时又实现了对operatorp[]下标运算符函数的重载。
但是这里又有一个问题,那就是假如传递进来的下标值是一个负数,也就是说用户不小心输入了一个负数,由于下标不能为负,并且编译器也不会限制用户输入一个负数,因此这回造成一个隐藏的错误。
解决的办法是在对传入的length加条判断语句,如:
if(length>len&&length<0)
return str[len-1];
或者直接将length定义为unsigned short int(无符号短整型),如:
char String::operaot[](unsigned short int length)const
这样,Sting类的私有成员变量len也要定义为无符号短整型变量,如:
unsigned short int len;
复制构造函数实现两个对象的赋值。
程序代码如下:
1.#include <iostream>
ing namespace std;
3.class String
4.{
5.public:
6. String();
7. String(const String&rs); //复制构造函数
8. String(const char *const ch);
9. char &operator[](unsigned short int length);
10. char operator[](unsigned short int length)const;
11. unsigned short int getlen()const{return len;}
12. const char *getstr()const{return str;}
13.private:
14. unsigned short int len;
15. char *str;
16.};
17.String::String()
18.{
19. len = 0;
20. str = new char[1];
21. str[0] = '\0';
22.}
23.String::String(const String&rs)
24.{
25. cout<<"复制构造函数执行....\n";
26. len = rs.getlen();
27. str = new char[len+1];
28. for(int i=0;i<len;i++)
29. str[i]=rs[i];
30. str[len]='\0';
31.}
32.String::String(const char *const ch)
33.{
34. len = strlen(ch);
35. str = new char[len+1];
36. for(int i=0;i<len;i++)
37. str[i] = ch[i];
38. str[len]='\0';
39.}
40.char &String::operator[](unsigned short int length)
41.{
42. cout<<"operator[]执行\n";
43. if(length>len)
44. return str[len-1];
45. else
46. return str[length];
48.char String::operator[](unsigned short int length)const
49.{
50. cout<<"operator[]const执行\n";
51. if(length>len)
52. return str[len-1];
53. else
54. return str[length];
55.}
56.int _tmain(int argc, _TCHAR* argv[])
57.{
58. String s1;
59. cout<<"s1:"<<s1.getstr()<<"\t 共"<<s1.getlen()<<"字符"<<endl;
60. String s2("study");
61. cout<<"s2:"<<s2.getstr()<<"\t 共"<<s2.getlen()<<"字符"<<endl;
62. char *temp = "help me";
63. String s3(temp);
64. cout<<"s3:"<<s3.getstr()<<"\t 共"<<s3.getlen()<<"字符"<<endl;
65. cin>>s3[-999];
66. cout<<"s3:"<<s3.getstr()<<"\t 共"<<s3.getlen()<<"字符"<<endl;
67. cout<<"s3[-999]:"<<s3[-999]<<endl;
68. String s4=s3;
69. cout<<"s4:"<<s4.getstr()<<"\t 共"<<s4.getlen()<<"字符
"<<endl;
70. system("pause");
71. return 0;
72.}
复制代码
用重载赋值运算符函数实现真正的字符串赋值功能
楼上介绍赋值其实不是真正的赋值,而是利用赋值构造函数实现的对一个新对象的初始化,本节我们来学习真正的赋值功能,该功能允许String类的两个对象直接进行赋值,如:String s1("hello"),s2("name");
s1 = s2;
也允许将未命名字符串直接赋给String类的一个对象,如:
s1 = "hello world";
为实现该功能,我们首先需要重载赋值运算符,因为默认的赋值运算符只能实现变量的赋值功能,不能实现对象之间的赋值操作。
我们前面学习了重载赋值运算符operator=,因此这里可以直接写出该函数。
1.String& String::operator =(const String &rs)
3. if(this==&s)
4. return *this;
5. delete []str;
6. len = rs.getlen();
7. str = new char[len+1];
8. for(int i=0;i<len;i++)
9. {
10. str[i] = rs[i];
11. }
12. str[len] = '\0';
13. return *this;
14.}
复制代码
该函数接受一个String类对象,并将该对象的别名修饰为const,传递到函数体中,在函数体第1行首先检测传递进来的对象是否与当前调用的对象相等,也就是等号左边的对象是否等于右边的对象。
假如相等,那么返回左边的对象,否则删除左边对象的str成员,也就是字符串成员。
然后根据右边字符串的长度创建一个字符串,再加1用于保存结束空字符。
接着将右边对象的字符串成员中的每个字符赋给左边对象的字符串成员,最后用空字符结束左边对象的字符串并按引用方式返回左边对象。
返回左边对象的别名是为下一次赋值做准备,如: s1 = s2 = s3;
从右侧开始将s3的值赋给s2后,返回s2的别名,然后再将s2的别名赋给s1,从而实现赋值符 = 的重载。
1.#include "stdafx.h"
2.#include <iostream>
ing namespace std;
4.class String
5.{
6.public:
7. String();
8. String(const char *const ch);
9. String(const String &rs);
10. char &operator[](unsigned short int length);
11. char operator[](unsigned short int length)const;
12. String &operator = (const String &rs);
13. int getlen()const{return len;}
14.private:
15. unsigned short int len;
16. char *str;
17.};
18.String::String()
19.{
20. len = 0;
21. str = new char[1];
22. str[0] = '\0';
23.}
24.String::String(const char *const ch)
25.{
26. len = strlen(ch);
27. str = new char[len+1];
28. for(int i=0;i<len;i++)
29. str[i] = ch[i];
30. str[len] = '\0';
31.}
32.String::String(const String &rs)
33.{
34. len = rs.getlen();
35. str = new char[len+1];
36. for(int i=0;i<len;i++)
37. str[i] = rs[i];
38. str[len] = '\0';
39.}
40.char &String::operator [](unsigned short int length)
41.{
42. if(length>len)
43. return str[len-1];
44. else
45. return str[length];
46.}
47.char String::operator [](unsigned short int length)const
48.{
49. if(length>len)
50. return str[len-1];
51. else
52. return str[length];
53.}
54.String& String::operator =(const String &rs)
55.{
56. if(this==&rs)
57. return *this;
58. delete []str;
59. len = rs.getlen();
60. str = new char[len+1];
61. for(int i=0;i<len;i++)
62. {
63. str[i] = rs[i];
65. str[len] = '\0';
66. return *this;
67.}
68.int _tmain(int argc, _TCHAR* argv[])
69.{
70. String s1 = "hello world";
71. String s2;
72. s2 = s1;
73. cout<<s2.getlen()<<endl;
74. s2 = "name";
75. cout<<s2.getlen()<<endl;
76. system("pause");
77. return 0;
78.}
用重载输出运算符operator<<()函数实现字符串的输出
在String类中增加operator<<()函数可以直接输出String类对象的字符串成员,因此我们可用该函数代替成员函数getlen(),该函数的定义与实现如下所示。
1. friend ostream &operator <<(ostream &o,const String &str)
2. {
3. o<<str.str;
4. return o;
5. }
复制代码
我们将函数添加到String类中,补充后的程序如下:
实现String类对象的输出。
1.#include "stdafx.h"
2.#include <iostream>
ing namespace std;
4.class String
5.{
6.public:
7. String();
8. String(const char *const ch);
9. String(const String &rs);
10. char &operator[](unsigned short int length);
11. char operator[](unsigned short int length)const;
12. String &operator = (const String &rs);
13. int getlen()const{return len;}
14. friend ostream &operator <<(ostream &o,const String &str)
16. o<<str.str;
17. return o;
18. }
19.private:
20. unsigned short int len;
21. char *str;
22.};
23.String::String()
24.{
25. len = 0;
26. str = new char[1];
27. str[0] = '\0';
28.}
29.String::String(const char *const ch)
30.{
31. len = strlen(ch);
32. str = new char[len+1];
33. for(int i=0;i<len;i++)
34. str[i] = ch[i];
35. str[len] = '\0';
36.}
37.String::String(const String &rs)
38.{
39. len = rs.getlen();
40. str = new char[len+1];
41. for(int i=0;i<len;i++)
42. str[i] = rs[i];
43. str[len] = '\0';
44.}
45.char &String::operator [](unsigned short int length)
46.{
47. if(length>len)
48. return str[len-1];
49. else
50. return str[length];
51.}
52.char String::operator [](unsigned short int length)const
53.{
54. if(length>len)
55. return str[len-1];
56. else
57. return str[length];
58.}
59.String& String::operator =(const String &rs)
60.{
61. if(this==&rs)
62. return *this;
63. delete []str;
64. len = rs.getlen();
65. str = new char[len+1];
66. for(int i=0;i<len;i++)
67. {
68. str[i] = rs[i];
69. }
70. str[len] = '\0';
71. return *this;
72.}
73.int _tmain(int argc, _TCHAR* argv[])
74.{
75. String s1 = "hello world";
76. String s2;
77. s2 = s1;
78. cout<<s2.getlen()<<endl;
79. s2 = "name";
80. cout<<s2.getlen()<<endl;
81. cout<<s2<<endl;
82. system("pause");
83. return 0;
84.}
复制代码
用重载输入运算符operator>>()函数实现字符串的输出
我们再为String类添加字符串的输入功能,该功能可以通过重载输入运算符operator>>()来实现.
1. friend istream &operator >>(istream &i,const String &str)
2. {
3. i>>str.str;
4. return i;
5. }
复制代码
我们将该函数添加到String类中,补充后的程序如下所示。
1.#include "stdafx.h"
2.#include <iostream>
ing namespace std;
4.class String
5.{
6.public:
7. String();
8. String(const char *const ch);
9. String(const String &rs);
10. char &operator[](unsigned short int length);
11. char operator[](unsigned short int length)const;
12. String &operator = (const String &rs);
13. int getlen()const{return len;}
14. friend ostream &operator <<(ostream &o,const String &str)
15. {
16. o<<str.str;
17. return o;
18. }
19. friend istream &operator >>(istream &i,const String &str)
20. {
21. i>>str.str;
22. return i;
23. }
24.private:
25. unsigned short int len;
26. char *str;
27.};
28.String::String()
29.{
30. len = 0;
31. str = new char[1];
32. str[0] = '\0';
33.}
34.String::String(const char *const ch)
35.{
36. len = strlen(ch);
37. str = new char[len+1];
38. for(int i=0;i<len;i++)
39. str[i] = ch[i];
40. str[len] = '\0';
41.}
42.String::String(const String &rs)
43.{
44. len = rs.getlen();
45. str = new char[len+1];
46. for(int i=0;i<len;i++)
47. str[i] = rs[i];
48. str[len] = '\0';
49.}
50.char &String::operator [](unsigned short int length)
51.{
52. if(length>len)
53. return str[len-1];
54. else
55. return str[length];
56.}
57.char String::operator [](unsigned short int length)const
58.{
59. if(length>len)
60. return str[len-1];
61. else
62. return str[length];
63.}
64.String& String::operator =(const String &rs)
65.{
66. if(this==&rs)
67. return *this;
68. delete []str;
69. len = rs.getlen();
70. str = new char[len+1];
71. for(int i=0;i<len;i++)
72. {
73. str[i] = rs[i];
74. }
75. str[len] = '\0';
76. return *this;
77.}
78.int _tmain(int argc, _TCHAR* argv[])
79.{
80. String s1;
81. cin>>s1;
82. cout<<s1;
83. system("pause");
84. return 0;
85.}
复制代码
用重载比较运算符实现字符串的比较。
我们可以用关键字operator来重载运算符 < > 和= 来实现对字符串的比较。
如:
1. friend bool operator <(const String &str1,const String
&str2)
2. {
3. if(strcmp(str1.str,str2.str)<0)
4. return 1;
5. else
6. return 0;
7. }
复制代码
第1行定义了一个返回bool值的operator<()函数,该函数的两个参数反别是待比较的两个对象的别名,将该函数说明友元,是为了String类对象也能与char型字符串比较,如:if("hello"<s);
那么该友元函数operator<()会首先调用String类的构造函数将hello转换为String类对象,然后再进行比较。
如:
if(operator<("hello",s)); //调用友元函数operator<()
if(operator<(String("hello"),s); //友元函数调用String类的构造函数将char型字符串变量转换为String类对象。
然后再对这两个String类对象中的成员进行判断,如:
if(strcmp(str1.str,str2.str)<0)
return 1;
else
return 0;
这里调用了C的字符串判断函数strcmp对两个对象中的字符串成员进行判断,按照字母序列,如果第1个字符串排列在第2个字符串之前,那么返回一个小于0的数,如果相同,返回0,如果排在第2个字符串之后,返回一个大于0的数。
因此我们用strcmp判断str1.str 与str2.str比较的结果是否小于0,如果小于0,那么str1.str小于str2.str,该条件为真,返回1,否则返回0。
另外,两个比较函数也可以用该方式来编写,如:
1. friend bool operator >(const String &str1,const String
&str2)
2. {
3. if(strcmp(str1.str,str2.str)>0)
4. return 1;
5. else
6. return 0;
7. }
复制代码
假如第1个字符串大于第2个字符串,返回1,否则,返回0。
1. friend bool operator ==(const String &str1,const String
&str2)
2. {
3. if(strcmp(str1.str,str2.str)==0)
4. return 1;
5. else
6. return 0;
7. }
复制代码
假如第1个字符串等于第2个字符串,返回1,否则,返回0。
实现字符串的比较功能。
程序代码如下。
1.#include "stdafx.h"
2.#include <iostream>
ing namespace std;
4.class String
5.{
6.public:
7. String();
8. String(const char *const ch);
9. String(const String &rs);
10. char &operator[](unsigned short int length);
11. char operator[](unsigned short int length)const;
12. String &operator = (const String &rs);
13. int getlen()const{return len;}
14. friend ostream &operator <<(ostream &o,const String &str)
15. {
16. o<<str.str;
17. return o;
18. }
19. friend istream &operator >>(istream &i,const String &str)
20. {
21. i>>str.str;
22. return i;
23. }
24. friend bool operator <(const String &str1,const String
&str2)
25. {
26. if(strcmp(str1.str,str2.str)<0)
27. return 1;
28. else
29. return 0;
30. }
31. friend bool operator >(const String &str1,const String
&str2)
32. {
33. if(strcmp(str1.str,str2.str)>0)
34. return 1;
35. else
36. return 0;
37. }
38. friend bool operator ==(const String &str1,const String
&str2)
39. {
40. if(strcmp(str1.str,str2.str)==0)
41. return 1;
42. else
43. return 0;
44. }
45.private:
46. unsigned short int len;
47. char *str;
48.};
49.String::String()
50.{
51. len = 0;
52. str = new char[1];
53. str[0] = '\0';
54.}
55.String::String(const char *const ch)
56.{
57. len = strlen(ch);
58. str = new char[len+1];
59. for(int i=0;i<len;i++)
60. str[i] = ch[i];
61. str[len] = '\0';
62.}
63.String::String(const String &rs)
64.{
65. len = rs.getlen();
66. str = new char[len+1];
67. for(int i=0;i<len;i++)
68. str[i] = rs[i];
69. str[len] = '\0';
70.}
71.char &String::operator [](unsigned short int length)
72.{
73. if(length>len)
74. return str[len-1];
75. else
76. return str[length];
77.}
78.char String::operator [](unsigned short int length)const
79.{
80. if(length>len)
81. return str[len-1];
82. else
83. return str[length];
84.}
85.String& String::operator =(const String &rs)
86.{
87. if(this==&rs)
88. return *this;
89. delete []str;
90. len = rs.getlen();
91. str = new char[len+1];
92. for(int i=0;i<len;i++)
93. {
94. str[i] = rs[i];
95. }
96. str[len] = '\0';
97. return *this;
98.}
99.int _tmain(int argc, _TCHAR* argv[])
100.{
101. String s1("aaa"),s2("bbb");
102. int check1 = s1 > s2;
103. int check2 = s1 < s2;
104. int check3 = s1=="aaa";
105. cout<<"s1>s2:"<<check1<<endl;
106. cout<<"s1<s2:"<<check2<<endl;
107. cout<<"s1==aaa:"<<check3<<endl;
108. system("pause");
109. return 0;
110.}
复制代码
注意:构造String类对象s1和s2时调用了第65行的构造函数,将s1与char型字符串进行判断时在友元函数operator==()中也调用了第65行的构造函数,该构造函数创建了一个未命名对象,并将char型字符串作为该对象的成员。
为String类添加字符串的相加功能
我们再为这个String类添加字符串的相加功能,即实现两个String类对象的相加,要做到这一点,我们必须重载相加运算符函数operator+(),该函数定义与实现如下:
1.String String::operator +(const String &rs)
2.{
3.int total = len+rs.getlen();
4.String temp(total);
5.int i,j;
6.for(i=0;i<len;i++)
7. temp[i] = str[i];
8.for(j=0;j<rs.getlen();j++,i++)
9. temp[i] = rs[j];
10.temp[total] = '\0';
11.return temp;
12.}
复制代码
operator+()函数接受一个修饰为const的String类对象的别名,并将该别名传递到函数体中,函数体的第1行首先计算两个字符串相加后的长度,len代表加好(+)左边的字符串长度,rs.getlen()代表加好右边的字符串长度。
total为两个字符串相加后的总长度。
第2行创建一个String类的临时对象,并把total作为该临时对象的初始化长度。
由于我们没有一个参数为整形的构造函数,因此我们还要重载一个,该函数的定义与实现如下:
1.String::String(const unsigned short int length)
2.{
3.len = length;
4.str = new char[length+1];
5.fot(int i=0;i<=length;i++)
6. str[i] = '\0';
7.}
复制代码
由于字符串的长度不可能为一个负数,因此第1行将构造函数:String(unsigned short int length)的参数length定义为一个无符号短整型变量,第3行将初始化的字符长度length 赋给String类的私有成员len,第4行在堆中创建长度为length+1的char型数组,再加1用来保存结果空字符。
然后循环用结束空字符填充该数组的每个元素,要注意for循环的退出条件是i<=length,而不是i<length。
这保证了会将该数组的所有元素都填充为空字符。