概述
C++ 语言重载运算符 - 输入和输出运算符
IO
标准库分别使用 >>
和 <<
执行输入和输出操作,IO
库定义了用其读写内置类型的版本,而类则需要自定义适合其对象的新版本以支持 IO
操作。
1. 重载输出运算符 <<
通常情况下,输出运算符的第一个形参是一个非常量 std::ostream
对象的引用。std::ostream
是非常量,因为向流写入内容会改变其状态。该形参是引用,因为我们无法直接复制一个 std::ostream
对象。
第二个形参一般来说是一个常量的引用,该常量是我们想要打印的类类型。第二个形参是引用,因为我们希望避免复制实参。该形参可以是常量,因为打印对象不会改变对象的内容。
为了与其他输出运算符保持一致,operator<<
一般要返回它的 std::ostream
形参。
1.1 Sales
的输岀运算符
std::ostream &operator<<(std::ostream &os, const Sales &item)
{
os << item.isbn() << " " << item.sold_num << " " << item.revenue << " " << item.avg_price();
return os;
}
打印一个 Sales
对象意味着要分别打印它的三个数据成员以及通过计算得到的平均销售价格,每个元素以空格隔开。完成输出后,运算符返回刚刚使用的 std::ostream
的引用。
1.2 输出运算符尽量减少格式化操作
用于内置类型的输岀运算符不太考虑格式化操作,尤其不会打印换行符。如果运算符打印了换行符,则用户就无法在对象的同一行内接着打印一些描述性的文本了。相反,令输出运算符尽量减少格式化操作可以使用户有权控制输出的细节。通常,输出运算符应该主要负责打印对象的内容而非控制格式,输出运算符不应该打印换行符。
1.3 输入输出运算符必须是非成员函数
与 std::iostream
标准库兼容的输入输出运算符必须是普通的非成员函数,而不能是类的成员函数。否则,它们的左侧运算对象将是我们的类的一个对象:
假设输入输出运算符是某个类的成员,则它们也必须是 std::istream
或 std::ostream
的成员。这两个类属于标准库,并且我们无法给标准库中的类添加任何成员。
如果我们希望为类自定义 IO
运算符,则必须将其定义成非成员函数。IO
运算符通常需要读写类的非公有数据成员,IO
运算符一般被声明为友元。
2. 重载输入运算符 >>
通常情况下,输入运算符的第一个形参是运算符将要读取的流的引用,第二个形参是将要读入到的 (非常量) 对象的引用。该运算符通常会返回某个给定流的引用。第二个形参必须是个非常量,因为输入运算符本身的目的就是将数据读入到这个对象中。
2.1 Sales
的输入运算符
std::istream &operator>>(std::istream &is, Sales &item)
{
double price;
is >> item.book_no >> item.sold_num >> price;
// check that the inputs succeeded
if (is) {
item.revenue = item.sold_num * price;
}
else {
// input failed: give the object the default state
item = Sales();
}
return is;
}
if
语句检查读取操作是否成功,如果发生了 IO
错误,则运算符将给定的对象重置为空 Sales
,确保对象处于正确的状态。输入运算符必须处理输入可能失败的情况,而输出运算符不需要。
2.2 输入时的错误
在执行输入运算符时可能发生下列错误:
- 当流含有错误类型的数据时读取操作可能失败。
- 当读取操作到达文件末尾或者遇到输入流的其他错误时也会失败。
在程序中我们没有逐个检査每个读取操作,而是等读取了所有数据后赶在使用这些数据前一次性检查:
// check that the inputs succeeded
if (is) {
item.revenue = item.sold_num * price;
}
else {
// input failed: give the object the default state
item = Sales();
}
如果读取操作失败,则 price
的值将是未定义的。在使用 price
前我们需要首先检查输入流的合法性,然后才能执行计算并将结果存入 revenue
。如果发生了错误,只要将一个新的默认初始化的 Sales
对象赋予 item
从而将其重置为空 Sales
就可以了。执行这样的赋值后,item
的 book_no
成员将是一个空 std::string
,revenue
和 sold_num
成员将等于 0。
如果在发生错误前对象已经有一部分被改变,则适时地将对象置为合法状态显得异常重要。通过将对象置为合法的状态,我们能 (略微) 保护使用者免于受到输入错误的影响。此时的对象处于可用状态,即它的成员都是被正确定义的。而且该对象也不会产生误导性的结果,因为它的数据在本质上确实是一体的。当读取操作发生错误时,输入运算符应该负责从错误中恢复。
2.3 标示错误
一些输入运算符需要做更多数据验证的工作。即使从技术上来看 IO
是成功的,输入运算符也应该设置流的条件状态以标示出失败信息。通常情况下,输入运算符只设置 failbit
。除此之外,设置 eofbit
表示文件耗尽,而设置 badbit
表示流被破坏。最好的方式是由 IO
标准库自己来标示这些错误。
transaction [trænˈzækʃn]:n. (一笔) 交易,业务,买卖,办理,处理
3. 重载运算符 - 输入和输出运算符
0-201-78345-X 3 20.00
0-201-78345-X 2 25.00
0-201-78345-X 5 110 22
47
0-201-78345-X 5 110 22
0-201-78345-X 7 160 22.8571
请按任意键继续. . .
Sales.h
//============================================================================
// Name : Yongqiang Cheng
// Author : Yongqiang Cheng
// Version : Version 1.0.0
// Copyright : Pearson Education, Inc.
// Description : Hello World in C++, Ansi-style
//============================================================================
#ifndef SALES_H
#define SALES_H
#include <iostream>
#include <string>
class Sales {
friend std::ostream &operator<<(std::ostream &, const Sales &);
friend std::istream &operator>>(std::istream &, Sales &);
friend bool operator==(const Sales &, const Sales &);
friend std::ostream &print(std::ostream &, const Sales &);
friend std::istream &read(std::istream &, Sales &);
public:
// constructors
Sales() = default;
Sales(const std::string &s) : book_no(s) { }
Sales(const std::string &s, unsigned int n, double p) : book_no(s), sold_num(n), revenue(p * n) { }
Sales(std::istream &);
std::string isbn() const { return book_no; }
Sales &operator+=(const Sales &);
private:
double avg_price() const;
std::string book_no;
unsigned int sold_num = 0;
double revenue = 0.0;
};
// non-member Sales operations
inline
bool CompareIsbn(const Sales &lhs, const Sales &rhs)
{
return lhs.isbn() < rhs.isbn();
}
inline
bool operator==(const Sales &lhs, const Sales &rhs)
{
return (lhs.isbn() == rhs.isbn()) && (lhs.sold_num == rhs.sold_num) && (lhs.revenue == rhs.revenue);
}
inline
bool operator!=(const Sales &lhs, const Sales &rhs)
{
return (!(lhs == rhs));
}
// old versions
Sales add(const Sales &, const Sales &);
std::ostream &print(std::ostream &, const Sales &);
std::istream &read(std::istream &, Sales &);
// new operator functions
Sales operator+(const Sales &, const Sales &);
std::ostream &operator<<(std::ostream &, const Sales &);
std::istream &operator>>(std::istream &, Sales &);
#endif
Sales.cpp
//============================================================================
// Name : Yongqiang Cheng
// Author : Yongqiang Cheng
// Version : Version 1.0.0
// Copyright : Pearson Education, Inc.
// Description : Hello World in C++, Ansi-style
//============================================================================
#include "Sales.h"
#include <string>
Sales::Sales(std::istream &is)
{
is >> *this; // read a transaction from is into this object
}
double Sales::avg_price() const
{
if (sold_num) {
return revenue / sold_num;
}
else {
return 0;
}
}
// member binary operator: left-hand operand is bound to the implicit this pointer
// assumes that both objects refer to the same book
Sales &Sales::operator+=(const Sales &rhs)
{
sold_num += rhs.sold_num;
revenue += rhs.revenue;
return *this;
}
// assumes that both objects refer to the same book
Sales operator+(const Sales &lhs, const Sales &rhs)
{
Sales sum = lhs; // copy data members from lhs into sum
sum += rhs; // add rhs into sum
return sum;
}
std::istream &operator>>(std::istream &is, Sales &item)
{
double price;
is >> item.book_no >> item.sold_num >> price;
// check that the inputs succeeded
if (is) {
item.revenue = item.sold_num * price;
}
else {
// input failed: give the object the default state
item = Sales();
}
return is;
}
std::ostream &operator<<(std::ostream &os, const Sales &item)
{
os << item.isbn() << " " << item.sold_num << " " << item.revenue << " " << item.avg_price();
return os;
}
// operators replace these original named functions
std::istream &read(std::istream &is, Sales &item)
{
double price = 0;
is >> item.book_no >> item.sold_num >> price;
item.revenue = price * item.sold_num;
return is;
}
std::ostream &print(std::ostream &os, const Sales &item)
{
os << item.isbn() << " " << item.sold_num << " " << item.revenue << " " << item.avg_price();
return os;
}
Sales add(const Sales &lhs, const Sales &rhs)
{
Sales sum = lhs; // copy data members from lhs into sum
sum += rhs; // add rhs into sum
return sum;
}
yongqiang.cpp
//============================================================================
// Name : Yongqiang Cheng
// Author : Yongqiang Cheng
// Version : Version 1.0.0
// Copyright : Copyright (c) 2020 Yongqiang Cheng
// Description : Hello World in C++, Ansi-style
//============================================================================
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <iostream>
#include <string>
#include "Sales.h"
int main()
{
Sales data1, data2;
std::cin >> data1 >> data2; // read Sales transactions
std::cout << data1 + data2 << std::endl; // write sum of Sales objects
std::cout << 42 + 5 << std::endl; // write sum of ints
// equivalent calls to a nonmember operator function
data1 + data2; // normal expression
operator+(data1, data2); // equivalent function call
std::cout << operator+(data1, data2) << std::endl;
data1 += data2; // expression-based call
data1.operator+=(data2); // equivalent call to a member operator function
std::cout << data1 << std::endl;
return 0;
}
References
(美) Stanley B. Lippman, (美) Josée Lajoie, (美) Barbara E. Moo 著, 王刚, 杨巨峰 译. C++ Primer 中文版[M]. 第 5 版. 电子工业出版社, 2013.
https://www.informit.com/store/c-plus-plus-primer-9780321714114
最后
以上就是温婉西装为你收集整理的C++ 语言重载运算符 - 输入和输出运算符 C++ 语言重载运算符 - 输入和输出运算符 的全部内容,希望文章能够帮你解决C++ 语言重载运算符 - 输入和输出运算符 C++ 语言重载运算符 - 输入和输出运算符 所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复