运算符重载(二)

基于上次String类的实现,这次继续学习其它相关运算符的重载来进一步完善String类,先将目前String类的实现贴出来,以下的实验都是基于它:
String.h:
#ifndef _STRING_H #define _STRING_H class String { public: explicit String(const char* str=""); String(const String& other);//深拷贝 String& operator = (const String& other);//赋值运算符重载实现深拷贝 String& operator = (const char* str);//赋值运算符重载 bool operator !() const;//非运算符重载 ~String(); void display() const; private: char* allocAndCpy(const char* str); char* str_; }; #endif // _STRING_H
String.cpp:
#pragma warning(disable:4996) #include "String.h" #include <string.h> #include <iostream> using namespace std; String::String(const char* str) { str_ = allocAndCpy(str); } String::String(const String& other) { str_ = allocAndCpy(other.str_); } String& String::operator = (const String& other) { if(this == &other) return *this; delete[] str_; str_ = allocAndCpy(other.str_); return *this; } String& String::operator = (const char* str) { delete[] str_; str_ = allocAndCpy(str); return *this; } char* String::allocAndCpy(const char* str) { int len = sizeof(str) + 1; char* newStr = new char[len]; memset(newStr, 0, len); strcpy(newStr, str); return newStr; } bool String::operator !() const { return strlen(str_) != 0; } String::~String() { delete[] str_; } void String::display() const { cout<<str_<<endl; }

先编写测试代码:

当然目前还编不过:

下面来重载该运算符:


编译运行:


说明代码是有BUG的,经过查找,是上次编写代码时写错了,如下:

应该改为:

这时再编译运行就一切正常用了:

另外有一个细节需要思考一下,[]运算符重载返回值为啥是一个char的引用呢?返回引用一是不用重新生成对象,另外一个原因如下:

看结果:

下面继续看下面这种情况:

编译:

这是由于没有实现const的[]运算符重载,所以下面来实现下:


编译运行:

但是存在一个问题:常量s2被更改了,这不是我们希望看到的:

要想如此,其实只要修改函数的返回值既可:


这时再编译:

这时将赋值注释掉,再编译:


但是这个运算符还有一些缺陷,那在哪呢?


这里需要用到之前学的static_cast,具体实现如下:


编译运行:

这时之前学过的const_cast函数就派上用场啦:

至此,[]运算符的重载就算是比较完美了!

这属于二元运算符的重载,既可以用成员函数的方式,也可以用友元的方式,但是推荐使用友元!在实现之前先编写测试代码:

当然现在没有实现+运算符,下面开始以友元的方式实现它:


编译运行:

无法转换构造了,这是什么原因呢?


再次编译运行:

从结果中来看不对,应该是“xxxyyy”,那是哪的问题呢?其实是代码是有问题的:

再次编译运行:

汗,居然还报错,仔细一看错误,低级呀,改正一下:

再次编译运行:

另外还支持这种加法运算:

编译运行:

这是为什么呢?这是由于我们用了友元方式实现:"aaa"可以转换构造成字符串对象的,因为友元的第一个参数s1是需要一个字符串对象,这就是为什么要推荐用友元实现的原因,如果采用成员对象的话,第一个参数引含的是对象指针,这就不行了,需要悉知,其实后面可以加任意多个字符串:

编译运行:

这又如何解释呢?

但是如果这样写:

编译运行:

这是由于没有重载带两个char*的+运算符,所以就编译不过了,所以前面必须有一个String对象,这样的写法才可以。
以上是+运算符重载的实现,在学习下一个运算符重载的时候,现有代码有个小地方需要优化一下,如下:

以上代码冗余了,所以有必要将其提取出来:


同样先编写测试代码:

当然现在编译会出错:

下面具体来实现:


编译运行:

而目前代码有可以优化的地方:

所以调整为:

这样代码就完美多了。

- C++的I/O流库的一个重要特性就是能够支持新的数据类型的输出和输入。
- 用户可以通过对插入符(<<)和提取符(>>)进行重载来支持新的数据类型。
- 流运算符的重载只能使用友元函数进行重载。
FAQ:为什么一定要使用友元函数进行重载?
因为第一个参数需要传流对象,而如果采用成员方式的话,第一个参数则成为了字符串自身了,不符合要求。 - friend istream& operator>>(istream&, 类类型&);
- friend ostream& operator<<(ostream&, const 类类型&);
对于目前的程序想打印一个String,都是通用去调用它的display()方法,有没有可以像平常打印输出的方式来输出呢,如下:

现在同样会报错:

实际上我们平常写的“cout”是一个ostream对象:

下面来实现:



编译运行:

需要包含头文件:

由于String.h已经包含了该头文件,所以在main.cpp中就没必要再包含了,所以可以去掉:

再次编译运行:


先来编写测试代码:
#include "String.h" int main(void) { String s1("abcdefg"); char ch = s1[1]; cout<<ch<<endl; s1[3] = 'D'; s1.display(); const String s2("xyzabc"); ch = s2[2]; //s2[2] = 'M'; s2.display(); String s3 = "xxx"; String s4 = "yyy"; String s5 = s3 + s4; s5.display(); String s6 = "aaa" + s3 + "bbb" + "cccc"; s6.display(); s3 += s4; s3.display(); cout<<"s3:"<<s3<<endl; String s7; cin>>s7; cout<<s7<<endl; return 0; }
编译:

下面最后来实现输入运算符:


编译运行:

浙公网安备 33010602011771号