You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++重载流插入与提取运算符作为成员函数的相关疑问

C++ 流运算符重载示例与问题解答

以下内容摘自Deitel所著《C++ How to Program(第10版)》,对应PhoneNumber类的流插入、流提取运算符重载实现代码如下:

示例代码

PhoneNumber.h 类定义

//Fig. 10.3: PhoneNumber.h
//PhoneNumber class definition
#ifndef PHONENUMBER_H
#define PHONENUMBER_H

#include <iostream>
#include <string>

class PhoneNumber
{
    friend std::ostream& operator<<(std::ostream&, const PhoneNumber&);
    friend std::istream& operator>>(std::istream&, PhoneNumber&);

private:
    std::string areaCode; //3-digit area code
    std::string exchange; //3-digit exchange
    std::string line; //4-digit line
};
#endif // PHONENUMBER_H

PhoneNumber.cpp 运算符重载实现

//Fig. 10.4: PhoneNumber.cpp
//Overloaded stream insertion and stream extraction operators
#include <iomanip>
#include "PhoneNumber.h"
using namespace std;

//overloaded stream insertion operator; cannot be a member function
//if we would like to invoke it with cout << somePhoneNumber;

ostream& operator<<(ostream& output, const PhoneNumber& number)
{
    output << "Area code: " << number.areaCode << "\nExchange: "
        << number.exchange << "\nLine: " << number.line << "\n"
        << "(" << number.areaCode << ") " << number.exchange << "-"
        << number.line << "\n";
    return output; //enables cout << a << b << c;
}

//overloaded stream extraction operator; cannot be a member function
//if we would like to invoke it with cin >> somePhoneNumber;
istream& operator>>(istream& input, PhoneNumber& number)
{
    input.ignore(); //skip (
    input >> setw(3) >> number.areaCode; //input area code
    input.ignore(2); //skip ) and space
    input >> setw(3) >> number.exchange; //input exchange
    input.ignore(); //skip dash (-)
    input >> setw(4) >> number.line; //input line
    return input; //enables cin >> a >> b >> c
}

fig10_5.cpp 测试主程序

//Fig. 10.5: fig10_5.cpp
//Demonstrating Class PhoneNumber's overloaded stream insertion
//and stream extraction operators.

#include <iostream>
#include "PhoneNumber.h"
#include <string>

using namespace std;

int main()
{
    PhoneNumber phone; //create object phone

    cout << "Enter phone number in the form (555) 555-5555:" << endl;

    //cin >> phone invokes operator>> by implicitly issuing
    //the non-member function call operator>>(cin, phone)
    cin >> phone;

    //cout << phone invokes operator<< bby implicitly issuing
    //the non-member function call operator<<(cout, phone)
    cout << phone << endl;
}

问题背景

已知规则:二元运算符的重载函数若作为类成员函数,左操作数必须是该成员函数所属类的对象,成员函数只能由作为左操作数的本类对象调用。
教材原文表述如下:

重载的流插入运算符(<<)使用时左操作数类型为ostream&,例如表达式cout << classObject。若要支持右操作数为用户自定义类对象的该种调用形式,<<运算符必须重载为非成员函数。若要将其重载为成员函数,operator<<必须是ostream类的成员。但用户自定义类场景下无法实现这一点,因为我们不允许修改C++标准库类。


问题解答

问题1:流插入运算符和流提取运算符是否分别属于ostream类和istream类?

  • 处理内置类型、标准库自带类型的基础版<</>>运算符,确实是ostream、istream类的成员函数。日常写cout << 123、cin >> int_value这类代码时,本质是调用流对象的成员函数:cout.operator<<(123)、cin.operator>>(int_value),完全符合成员运算符重载的规则。
  • 支持用户自定义类型的<</>>重载版本,不属于ostream/istream类。标准库实现时不可能预知所有用户会定义哪些自定义类型,自然不可能提前把对应重载写入流类的定义中。

问题2:教材加粗表述的具体含义

  • 先明确二元运算符的重载解析规则:如果二元运算符@的重载是成员函数,那么表达式a @ b会被编译器解释为a.operator@(b),也就是说operator@必须是左操作数a所属类的成员函数。
  • 对应到cout << phone这个表达式,左操作数a是ostream类型的cout,右操作数b是自定义类型PhoneNumber的对象。如果要把这个版本的<<重载为成员函数,就必须把接收PhoneNumber参数的operator<<函数写到ostream类的内部,成为ostream的成员。
  • 但C++标准明确禁止修改标准库类的定义,直接改动标准库头文件中ostream、istream的代码属于未定义行为,也完全不具备可移植性——不可能要求所有编译代码的用户都修改自己本地的标准库文件。
  • 你猜测的“无法向C++标准库类中添加自定义函数”是完全正确的:如果要走成员函数重载流运算符的路线,确实必须把对应operator<<加到ostream类中,但这条路在自定义类型场景下完全走不通,所以行业通用做法是把自定义类型的流运算符重载为全局非成员函数,同时将其声明为自定义类的友元以访问类的私有成员,和示例代码的实现一致。

内容的提问来源于stack exchange,提问作者WorldTreeBoy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 12:09:18