C++ 输入输出运算符重载 感想

原创 2017年03月14日 17:39:29

在C++中,经常会对输入输出运算符进行重载,而在重载的时候用到了友元(Friends)和引用返回(Returning References),这里对为什么会这么用发表一些思考。
比如,下面的类是一个简单的Student类,其中重载了<<>>

//
// Created by lgl on 17-3-14.
//
#include <iostream>
#include <string>
#include <iostream>
using namespace std;

class Student{
    string name;
    int age;
    string country;

public:
    Student(){}
    Student(string name, int age, string country){
        this->name = name;
        this->age = age;
        this->country = country;
    }

    friend ostream &operator << (ostream &os, const Student &stu){
        os << "Name: " << stu.name <<
                "; Age: " << stu.age << "; Country: " << stu.country << ".";
        return os;
    }

    friend istream &operator >> (istream &is, Student &stu){
        is >> stu.name >> stu.age >> stu.country;
        return is;
    }
};

int main()
{
    Student s("xiaoMing", 15, "China");
    Student t;
    cin >> t;
    cout << s << endl;
    cout << t << endl;
    return 0;
}

运行:

输入:
xiaoQiang 78 Earth
输出:
Name: xiaoMing; Age: 15; Country: China.
Name: xiaoQiang; Age: 78; Country: Earth.

返回引用

先说一下为什么会返回引用。
当函数返回引用类型时,没有复制返回值,返回的是对象本身,比如:

const string &shorterString(const string &s1,const string &s2)
{
    return s1.size() < s2.size() ? s1 : s2;
}

这里参数s1和s2均没有发生拷贝,直接传递了引用,而在返回的时候,也直接返回了较短的字符串的引用,整个过程中都没有发生字符串的拷贝,节省了资源,比较高效。
所以对于返回引用,要求必须在函数的参数中,包含有以引用方式或指针方式存在的,需要被返回的参数。如果两个参数都不是引用,则两个参数实际上都是放在函数栈里的局部变量,返回他们的引用类型会造成不可预知的错误(因为函数结束运行时,栈地址被回收了,此时这些地址上的数据可以被其他量覆盖,再次使用这些地址上的数据,已经不是原来的那个值了。)
比如:

int &smallInt(int a, int b)
{
    return a < b ? a : b;
}

编译时会发出警告警告:函数可能返回局部变量的地址 [-Wreturn-local-addr] return a < b ? a : b;。(但是如果形参是string则不会发出警告,原因应该是string属于字符串常量,储存在只读数据段,return str只是返回了该字符串在只读数据段所在的首地址,当函数退出后,该字符串所在的内存不会被回收,所以是正常的。)
关于这些解释的验证,详情见附1。
关于返回值的问题,具体可以参看函数中局部变量的返回这篇文章,通过几个例子会对返回值和返回引用有更深的理解。
最后,我们再看ostream &operator <<其实就是在返回ostream的引用,所以形参用的也是ostream的引用ostream & os,而之所以用引用是因为如果不使用引用返回,我们就只能使用值返回,值返回需要复制返回的对象,但是我们无法直接复制一个ostream对象(C++ Primer, 5th Edition, p494),所以不能使用值返回,只能使用引用返回。当然,也可以认为<<操作的结果是一个可以递进操作的左值,比如(cout << "haha") << "hehe"如果是临时对象,就不会有递进操作的能力

友元

那么为什么要使用友元呢。因为>><<的前置对象是cincout,不是Student类,所以不能把输入输出操作符设计成Student的类成员,而只能设计成普通函数。但是我们又想让IO运算符读写Student类的非公有数据成员,因此自然用到了友元。

通过上面的介绍,我们也就可以记住:重载输入输出运算符需要返回引用,因此参数也只能是引用。同时需要将函数定义为友元。理解了这些特征,就很容易写出重载函数了。

附1

#include <iostream>

using namespace std;

string &smallString(string a, string b)
{
    return a.size() < b.size() ? a : b;
}

int &smallInt(int a, int b)
{
    return a < b ? a : b;
}

int main()
{
    int &d = smallInt(3, 5);
    string &s = smallString("xixi", "hahaha");
    cout << d << endl;
    cout << s << endl;
    return 0;
}

函数运行结果为:

32744
xixi

再次运行为:

32598
xixi

很明显int的值在不停变动。如果我们再增添一个函数doNothing,并在调用smallInt之后进行调用:

#include <iostream>

using namespace std;

string &smallString(string a, string b)
{
    return a.size() < b.size() ? a : b;
}

int &smallInt(int a, int b)
{
    return a < b ? a : b;
}

void doNothing(int a, int b)
{
    a < b ? a : b;
}

int main()
{
    int &d = smallInt(3, 5);
    string &s = smallString("xixi", "hahaha");
    doNothing(44, 55);
    cout << d << endl;
    cout << s << endl;
    return 0;
}

则程序稳定输出:

44
xixi

即:原来属于3的地址,现在被填上了44。smallInt函数只返回了3所在的地址,后来在调用doNothing的时候,这个地址的内容被44覆盖了,那么输出d的时候,自然就是44。
这很符合之前关于返回引用部分的解释。

版权声明:本文为博主原创文章,未经博主允许不得转载。

C++面向对象操作符重载:输入输出操作符

1、在定义一个类的时候,合理的将操作符进行重载,可以像使用内置类型一样使用我们定义的类。 2、操作符重载有一些原则,我们必须谨记才能避免出错。在之前的章节中有涉及到操作符重载的案例,但是没有系统的讲,...
  • QQrenzai
  • QQrenzai
  • 2015年11月09日 08:38
  • 376

重载输入输出运算符

原文链接:http://blog.csdn.net/lyh__521/article/details/49601489重载输入输出运算符 我们平时可以用流 std::coutn ; 输出、输入字符...
  • lyh__521
  • lyh__521
  • 2015年11月03日 01:15
  • 2503

c++输入输出重载,赋值,加法运算符重载

在类里面声明了构造函数,但是没有写出它的实现,则在运行的时候会出现 error LNK2001: unresolved external symbol "public: __thiscall MyC...
  • nocodelife
  • nocodelife
  • 2013年02月28日 11:36
  • 4897

C++重载运算符(一)如何重载运算符

刚学C++的同学都老是听说什么运算符重载,好像很高级的样子,那么嘿嘿嘿。 现在我们有一个时间类,我们要重载加减乘除运算符,以达到时间类之间的计算就跟数字加减乘除运算一样。 首先我们先从最简单的来: 时...
  • lishuzhai
  • lishuzhai
  • 2016年02月29日 11:47
  • 3470

【C++】String类中的运算符重载

模块化设计: 头文件: #ifndef operator_operator_h #define operator_operator_h #include #include ...
  • Irean_Lau
  • Irean_Lau
  • 2015年06月07日 14:29
  • 6051

C++中不能重载的运算符

C++中不能重载的运算符:“?:”、“.”、“::”和“sizeof”,原因如下: 在具体讲解各个运算符不能重载之前,先来说明下【重载】:重载的本意是让操作符可以有新的语义,而不是更...
  • captain_wangnb
  • captain_wangnb
  • 2016年01月10日 19:37
  • 2783

C++运算符重载详解

为什么要对运算符进行重载: C++预定义中的运算符的操作对象只局限于基本的内置数据类型,但是对于我们自定义的类型(类)是没有办法操作的。但是大多时候我们需要对我们定义的类型进行类似的运算,这个时候就需...
  • lishuzhai
  • lishuzhai
  • 2016年03月02日 20:45
  • 24142

流运算符为什么不能重载为成员函数,只能用友元函数重载

一、 为什么operator 如果是重载双目操作符(即为类的成员函数),就只要设置一个参数作为右侧运算量,而左侧运算量就是对象本身。。。。。。 而 >>  或 如果一定要声明为成员函数,只...
  • snowsnowsnow1991
  • snowsnowsnow1991
  • 2015年12月15日 14:22
  • 2317

C++回顾之前置++、后置++、不等号!及赋值运算符重载

关于前置++,后置++,非(不等号)运算符及赋值运算符的重载
  • ab198604
  • ab198604
  • 2014年02月24日 16:44
  • 8582

C++运算符重载(11) - 重载<<和>>操作符

运算符 “>”用于流输出。 在开始重载这些操作符之前,必须注意下面的事项: 1) cout是输出类的对象,而cin是输入类的对象 2) 这些操作符必须重载为全局函数。如果想要让它们可以访问私有成员,...
  • shltsh
  • shltsh
  • 2015年05月27日 00:11
  • 787
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C++ 输入输出运算符重载 感想
举报原因:
原因补充:

(最多只允许输入30个字)