运算符重载(二)

基于上次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;
}

编译:

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

编译运行:

 

posted on 2016-04-12 22:59  cexo  阅读(197)  评论(0)    收藏  举报

导航