我们经常利用string类实例化对象来对字符串进行各种操作,string类是一个实用的类,那么对于string类的一些基本操作是如何实现的呢?我们简单的实现一下mystring的封装。
首先我们给出类模板:
其中主要的操作有:1,构造函数的实现 2,运算符的重载 。
对于string类的基本实现,我们知道string类是对字符串的操作,因此私有变量是字符指针和字符串的大小这两个成员,用来表示字符。其次就是对于字符指针,在这里我们为字符指针是专门开辟一片空间用来存放字符串的。
class mystring { friend::ostream& operator<<(ostream& out, mystring a); friend::istream& operator>>(istream& in, mystring &a); public: mystring();//默认构造 mystring(char* arr);//有参构造 mystring(const mystring& p);//拷贝构造 ~mystring(); int getsize() { return size; }//获取大小 //实现对字符串的访问,重载[] //这里将返回值进行引用,之后可以改变原本身的元素 char &operator[](int index) { //判断位置是否合法 if (index > 0 && index < size) { return arr[index]; } else { cout << "下标越界" << endl; exit(-1); } } //成员函数实现 mystring operator+(mystring&a); mystring operator+ (char* str); mystring operator=(mystring a); mystring operator=(char* str); bool operator>(mystring a); bool operator<(mystring a); bool operator==(mystring a); private: char* arr; int size; };
其次对于成员函数我们在函数未定义,对于全局函数,例如输入输出我们需要设置为友元函数来实现对类中成员的访问。
1.常用的字符串函数
strcpy
常用的字符串拷贝函数,将右边的const char*的字符串赋值给左边的字符串。
strcat
字符串追加函数,将右边的字符串追加给左边字符串的末尾,也就是连接两个字符串。
将该字符串中的前多少元素初始化为什莫,比如我们将实现对申请的一片字符指针arr的空间初始化为0,memset(arr,0,strlen(arr)+1),将arr中的所有字符初始化为0.
2.构造函数的创建
1.无参构造
无参构造主要是将字符指针初始化为空,大小为零。
mystring::mystring()
{
arr = NULL;
size=0;
}
2.有参构造
有参构造传递字符串以实现初始化类中的字符指针,以及对大小的确定。
mystring::mystring( char* arr) { this->arr = new char[strlen(arr)+1];//申请空间 memset(this->arr, 0, strlen(arr)+1);//初始化全设置为0 strcpy(this->arr, arr);//实现拷贝 size = (int)strlen(arr) ; }
注意这里是给指针专门开辟空间,所以对于指针在析构时,我们需要释放空间。
3.拷贝构造
拷贝原类改变为新类
mystring::mystring(const mystring& p) { size = p.size; this->arr = new char[size+ 1]; memset(arr, 0, strlen(p.arr) + 1);//初始化全设置为0 strcpy(this->arr,p.arr);//实现拷贝 }
拷贝构造实现形式与构造函数的形式一样。
析构
mystring ::~mystring() { if (arr != NULL) { delete[]arr; arr = NULL; } }
因为是对指针开辟空间,对应要释放空间。
3.运算符的重载
1.重载输出运算符
//重载输出运算符(全局重载) ostream& operator<<( ostream&out,mystring a) { out << a.arr ; return out; }
与之前学习到的重载输出运算符一样,返回类型为输出流,操作对象是mystring类。
注意对于输入与输出运算符的重载,我们都需要将返回值引用,实现对输入输出的所有操作。
2.重载输入运算符
重载输入运算符需要注意,原类中的字符指针是否为空,不为空要置空,然后再创建一个mystring作为媒介实现输入,之后再赋值,反回输入流。
//重载输入运算符 istream& operator>>(istream&in, mystring &a) { char buffer[100]; in >> buffer; //判断是否为空 if (a.arr != NULL) { delete[]a.arr; a.arr = NULL; } a.size =(int)strlen(buffer); a.arr = new char[a.size+1]; //置空 memset(a.arr, 0, a.size + 1);//注意这里不能用strlen(a.arr),在这里是未知访问 strcpy(a.arr, buffer); return in; }
3.重载+运算符
+运算符在string类中的操作是字符串的连接。
这里需要注意的是,我们是对字符串类的对象与字符串类+重载的实现,字符串类的对象与字符串+重载也是需要实现的,因为字符串类实例化的对象我们是将他当作字符串用的。
字符串类对象间的重载与字符指针的重载是类似的,主要是对其中的字符串的实现。
//字符串与类对象+的重载 mystring mystring::operator+(char* str) { mystring str1; str1.size = (int)strlen(str)+this->size; str1.arr = new char[str1.size + 1]; memset(str1.arr, 0, str1.size); strcpy(str1.arr, this->arr); strcat(str1.arr, str); return str1; }
//重载加法运算符 类与类之间+重载 mystring mystring:: operator+(mystring& a) { mystring str1;//作为媒介 str1.size = this->size+a.size; str1.arr = new char[str1.size+1]; memset(str1.arr, 0, str1.size); strcpy(str1.arr, this->arr); strcat(str1.arr, a.arr); return str1; }
两者的重载都是创建第三个类作为媒介,用来作为连接字符串后的字符串的载体,也是所返回的对象。因此在开辟空间的时候,需要注意空间的大小。
4.重载赋值运算符
string类中赋值运算符的作用就是字符串拷贝,但是是深度拷贝,所以我们需要开辟一片空间,对于类原this中所指向的空间实现地址的交换,即空间的交换。
还需要注意的是,这里的操作对象可以是类的对象值之间,也可以是类的对象直接与字符串直接的运算。
重载赋值运算符(只有指针成员时,需要深拷贝) mystring mystring:: operator=(mystring a) { //先判断是否为空 if (this->arr != NULL) { delete[]arr; arr = NULL; } if (this->arr != NULL) { delete[]arr; arr = NULL; } //这里是深拷贝,即地址之间的交换 this->size = a.size; this->arr = new char[a.size+1]; memset(this->arr, 0, a.size); strcpy(this->arr, a.arr); return *this; }
这里在赋值时,我们还是需要判断一下这里的原this中的指针是否为空,不为空,我们要将它置空之后再进行拷贝。
mystring mystring::operator=(char* str) { if (this->arr != NULL) { delete[]arr; arr = NULL; } //这里是深拷贝,即空间之间的交换 this->arr = new char[strlen(str)+1]; memset(this->arr, 0, strlen(str)); strcpy(this->arr, str); this->size = strlen(str); return *this; }
5.重载比较运算符
重载比较运算符是比较简单的,即> < ==,显然他们的返回值都布尔型的。
重载>运算符
//重载>运算符 bool mystring:: operator>(mystring a) { if (this->size > a.size) { return true; } else { return false; } }
重载<运算符
//重载<运算符 bool mystring:: operator<(mystring a) { if (this->size < a.size) { return true; } else { return false; } }
重载==运算符
//重载==运算符 bool mystring:: operator==(mystring a) { if (this->size == a.size) { return true; } else { return false; } }
如下是我所给出的操作用例,
int main() { mystring str=(char *)"hello world"; //mystring str ((char*)"hello world"); cout << str << endl; mystring str1 = (char*)"haha"; cout << str+str1 << endl; cout << str + (char *)"haha" << endl; str = str1; cout << str << endl; str = (char*)"heiehi"; cout << str << endl; if (str >str1) { cout << "str大与str1" << endl; } else { cout << "str小与str1" << endl; } }
好了,对于string的简单的实现就完成了,在此基础上我们还可以完成他的所有操作,在cplusplus上我们可以看到string所给出的定义string - C++ Reference (cplusplus.com)
STL中给出了string类的标准定义,以尽可能满足我们对字符串的某些操作,这些操作大家可以下去慢慢实现。