《C++ Primer》第7章 7.1节习题答案

《C++ Primer》第7章 类

7.1节 定义抽象数据类型

练习7.1:使用2.6.1节练习定义的Sales_data类为1.6节(第21)的交易处理程序编写一个新版本。
【出题思路】
程序的思路是:只要ISBN相同,就不断累加销量并重新计算平均售价,直至输入新的书籍为止。
【解答】
满足题意的程序如下所示:

//Sales_data.h

#ifndef SALES_DATA_H
#define SALES_DATA_H

// Definition of Sales_item class and related functions goes here
#include <iostream>
#include <string>


class Sales_data {

// these declarations are explained section 7.2.1, p. 270
// and in chapter 14, pages 557, 558, 561
friend std::istream& operator >> (std::istream&, Sales_data&);
friend std::ostream& operator << (std::ostream&, const Sales_data&);
friend bool operator < (const Sales_data&, const Sales_data&);
friend bool operator == (const Sales_data&, const Sales_data&);

public:
    // constructors are explained in section 7.1.4, pages 262 - 265
    // default constructor needed to initialize members of built-in type
    Sales_data() = default;
    Sales_data(const std::string &book): bookNo(book) { }
    Sales_data(std::istream &is) { is >> *this; }

public:
    // operations on Sales_item objects
    // member binary operator: left-hand operand bound to implicit this pointer
    Sales_data& operator += (const Sales_data&);

    // operations on Sales_item objects
    std::string isbn() const { return bookNo; }
    double avg_price() const;
// private members as before
private:
    std::string bookNo;      // implicitly initialized to the empty string
    unsigned units_sold = 0; // explicitly initialized
    double revenue = 0.0;
};


// used in chapter 10
inline bool compareIsbn(const Sales_data &lhs, const Sales_data &rhs)
{
    return lhs.isbn() == rhs.isbn();
}


// nonmember binary operator: must declare a parameter for each operand
Sales_data operator + (const Sales_data&, const Sales_data&);


inline bool operator == (const Sales_data &lhs, const Sales_data &rhs)
{
    // must be made a friend of Sales_item
    return lhs.units_sold == rhs.units_sold &&
           lhs.revenue == rhs.revenue &&
           lhs.isbn() == rhs.isbn();
}


inline bool operator != (const Sales_data &lhs, const Sales_data &rhs)
{
    return !(lhs == rhs); // != defined in terms of operator==
}


// assumes that both objects refer to the same ISBN
Sales_data& Sales_data::operator += (const Sales_data& rhs)
{
    units_sold += rhs.units_sold;
    revenue += rhs.revenue;
    return *this;
}


// assumes that both objects refer to the same ISBN
Sales_data operator + (const Sales_data& lhs, const Sales_data& rhs)
{
    Sales_data ret(lhs);  // copy (|lhs|) into a local object that we'll return
    ret += rhs;           // add in the contents of (|rhs|)
    return ret;           // return (|ret|) by value
}


std::istream& operator >> (std::istream& in, Sales_data& s)
{
    double price;
    in >> s.bookNo >> s.units_sold >> price;
    // check that the inputs succeeded
    if (in)
        s.revenue = s.units_sold * price;
    else
        s = Sales_data();  // input failed: reset object to default state
    return in;
}


std::ostream& operator << (std::ostream& out, const Sales_data& s)
{
    out << s.isbn() << " " << s.units_sold << " "
        << s.revenue << " " << s.avg_price();
    return out;
}


double Sales_data::avg_price() const
{
    if (units_sold)
        return revenue/units_sold;
    else
        return 0;
}
#endif // SALES_DATA_H
#include <iostream>
#include "Sales_data.h"
using namespace std;

int main() {
    cout << "请输入交易记录(ISBN, 销售量,原价,实际售价):" << endl;

    Sales_data total;  //保存下一条交易记录的变量
    //读入并处理剩余交易记录
    if(cin >> total)
    {
        Sales_data trans;
        while (cin >> trans)
        {
            //如果我们仍在处理相同的书
            if(total.isbn() == trans.isbn())
            {
                total += trans;  //更新总销售额
            }
            else
            {
                //打印前一本书的结果
                cout << "前一本书的结果:" << total << endl;
                total = trans;  //total现在表示下一本书的销售额
            }
        }
        cout << "total:" << total << endl;   //打印最后一本书的结果
    }
    else
    {
        //没有输入!警告读者
        cerr << "No data!" << endl;
        return -1; //表示失败
    }

    return 0;
}

运行结果:

练习7.2:曾在2.6.2节的练习(第67页)中编写一个Sales_data类,请向这个类添加combine和isbn成员。
【出题思路】
声明和定义成员函数的方式与普通函数差不多。成员函数的声明必须在类的内部,它的定义可以在类的内部,也可以在类的外部。
【解答】
添加combine和isbn 成员后的Sales_data类是:

 

//定义公有函数成员
public:
    // isbn函数只有一条语句,返回bookNo
    std::string isbn() const { return bookNo; }
    //combine函数用于把两个ISBN相同的销售记录合并在一起
    Sales_data& combine(const Sales_data &rhs)
    {
        units_sold += rhs.units_sold;                   //累加书籍的销售量
        saleprice = (rhs.saleprice * rhs.units_sold + saleprice * units_sold)
                    / (rhs.units_sold + units_sold);    //重新计算实现销售价格
        discount = saleprice / sellingprice;            //重新计算实际折扣
        return *this;                                   //返回合并后的结果
    }
    
// 定义私有数据成员
private:
    std::string bookNo;             // 书籍编号,隐式初始化为空串
    unsigned units_sold = 0;        // 销售量,显式初始化为0
    double sellingprice = 0.0;      // 原始价格,显式初始化为0.0
    double saleprice = 0.0;         // 实售价格,显式初始化为0.0
    double discount = 0.0;          // 折扣,显式初始化为0.0

练习3:修改7.1.1节(第229页)的交易处理程序,令其使用这些成员。

【出题思路】

该程序的作用是累加相同编号的书籍销售记录并输出,直至遇到下一个编号为止。改写程序时,用上一题定义的isbn函数获取书籍的编号,用combine函数把两条销售记录相加。

【解答】

满足题意的程序如下所示:

#ifndef SALES_DATA_H
#define SALES_DATA_H

#include <iostream>
#include <string>

class Sales_data {
    
// these declarations are explained section 7.2.1, p. 270 
// and in chapter 14, pages 557, 558, 561
friend std::istream& operator >> (std::istream&, Sales_data&);
friend std::ostream& operator << (std::ostream&, const Sales_data&);
friend bool operator < (const Sales_data&, const Sales_data&);
friend bool operator == (const Sales_data&, const Sales_data&);
    
public:
    // constructors are explained in section 7.1.4, pages 262 - 265
    // default constructor needed to initialize members of built-in type
    Sales_data() = default;
    Sales_data(const std::string &book): bookNo(book) { }
    Sales_data(std::istream &is) { is >> *this; }


//定义公有函数成员
public:
    // operations on Sales_item objects
    // member binary operator: left-hand operand bound to implicit this pointer
    Sales_data& operator += (const Sales_data&);
    
    // operations on Sales_item objects
    double avg_price() const;
    std::string isbn() const { return bookNo; }
    //combine函数用于把两个ISBN相同的销售记录合并在一起
    Sales_data& combine(const Sales_data &rhs)
    {
        units_sold += rhs.units_sold;                   //累加书籍的销售量
        //std::cout << "units_sold==================" << units_sold << std::endl;
        saleprice = (rhs.saleprice * rhs.units_sold + saleprice * units_sold)
                    / (rhs.units_sold + units_sold);    //重新计算实现销售价格
        discount = saleprice / sellingprice;            //重新计算实际折扣
        revenue += rhs.revenue;                         //总销售额
        return *this;                                   //返回合并后的结果
    }
    
// 定义私有数据成员
private:
    std::string bookNo;             // 书籍编号,隐式初始化为空串
    unsigned units_sold = 0;        // 销售量,显式初始化为0
    double sellingprice = 0.0;      // 原始价格,显式初始化为0.0
    double saleprice = 0.0;         // 实售价格,显式初始化为0.0
    double discount = 0.0;          // 折扣,显式初始化为0.0
    double revenue = 0.0;           // 总收入
};


// used in chapter 10
inline bool compareIsbn(const Sales_data &lhs, const Sales_data &rhs)
{
    return lhs.isbn() == rhs.isbn();
}


// nonmember binary operator: must declare a parameter for each operand
Sales_data operator + (const Sales_data&, const Sales_data&);


inline bool operator == (const Sales_data &lhs, const Sales_data &rhs)
{
    // must be made a friend of Sales_item
    return lhs.units_sold == rhs.units_sold &&
           lhs.revenue == rhs.revenue &&
           lhs.isbn() == rhs.isbn();
}


inline bool operator != (const Sales_data &lhs, const Sales_data &rhs)
{
    return !(lhs == rhs); // != defined in terms of operator==
}


// assumes that both objects refer to the same ISBN
Sales_data& Sales_data::operator += (const Sales_data& rhs)
{
    units_sold += rhs.units_sold; 
    revenue += rhs.revenue; 
    return *this;
}


// assumes that both objects refer to the same ISBN
Sales_data operator + (const Sales_data& lhs, const Sales_data& rhs)
{
    Sales_data ret(lhs);  // copy (|lhs|) into a local object that we'll return
    ret += rhs;           // add in the contents of (|rhs|) 
    return ret;           // return (|ret|) by value
}


//接收4个参数分别为:ISBN,销售量,原价,实际售价
std::istream& operator >> (std::istream& in, Sales_data& s)
{


    in >> s.bookNo >> s.units_sold >> s.sellingprice >> s.saleprice;
    // check that the inputs succeeded
    if (in)
        s.revenue = s.units_sold * s.saleprice;
    else 
        s = Sales_data();  // input failed: reset object to default state
    return in;
}


std::ostream& operator << (std::ostream& out, const Sales_data& s)
{
    out << s.isbn() << " " << s.units_sold << " "
        << s.revenue << " " << s.avg_price();
    return out;
}


double Sales_data::avg_price() const
{
    if (units_sold) 
        return revenue / units_sold;
    else 
        return 0;
}
#endif
#include <iostream>
#include "Sales_data.h"
using namespace std;

int main()
{
    cout << "请输入交易记录(ISBN),销售量,原价,实际售价:" << endl;
    Sales_data total;//保存下一条交易记录的变量
    //读入第一条交易记录,并确保有数据可以处理
    if(cin >> total)
    {
        Sales_data trans;//保存和的变量
        //读入并处理剩余交易记录
        while(cin >> trans)
        {
            //如果我们仍在处理相同的书
            if(total.isbn() == trans.isbn())
            {
                total.combine(trans); //更新总销额
            }
            else
            {
                //打印前一本书的结果
                cout << total << endl;
                total = trans;//total现在表示下一本书的销售额
            }
        }
        cout << total << endl;//打印最后本书的结果
    }
    else
    {
        //没有输入! 警告读者
        cerr << "No data?!" << endl;
        return -1;//表示失败
    }
    
    return 0;
}
#include <iostream>
#include "Sales_data.h"
using namespace std;

int main()
{
    cout << "请输入交易记录(ISBN),销售量,原价,实际售价:" << endl;
    Sales_data total;//保存下一条交易记录的变量
    //读入第一条交易记录,并确保有数据可以处理
    if(cin >> total)
    {
        Sales_data trans;//保存和的变量
        //读入并处理剩余交易记录
        while(cin >> trans)
        {
            //如果我们仍在处理相同的书
            if(total.isbn() == trans.isbn())
            {
                total.combine(trans); //更新总销额
            }
            else
            {
                //打印前一本书的结果
                cout << total << endl;
                total = trans;//total现在表示下一本书的销售额
            }
        }
        cout << total << endl;//打印最后本书的结果
    }
    else
    {
        //没有输入! 警告读者
        cerr << "No data?!" << endl;
        return -1;//表示失败
    }

    return 0;
}

运行结果:

 

练习7.4:编写一个名为Person的类,使其表示人员的姓名和信址,使用string对象存放这些元素,接下来的练习不断充实这个类的其他特征。

【出题思路】

练习定义类并添加必要的数据成员。

【解答】

满足题意的Person类是:

class Person
{
private:
    std::string strName;
    std::string strAddress;
};

练习5:在你的Person 类中提供一些操作使其能够返回姓名和住址。这些函数是否应该是const的呢,解释原因。
【出题思路】
练习向类添加函数成员的方法,理解常量成员函数。
【解答】
修改后的Person类是:

class Person
{
public:
    string getName() const { return strName; }
    string getAddress() const {return strAddress;}
private:
    std::string strName;
    std::string strAddress;
};

上述两个函数应该被定义成常量成员函数,因为不论返回姓名还是返回地址,函数体内都是只读取数据成员的值,而不会做任何改变。

练习6:对于函数add,read和print,定义你自已的版本
【出题思路】
参考书中的示例,定义自己的版本,注意读入和输出的具体信息应与类的数据成员保持一致。
【解答】
满足题意的add、read和print函数分别如下所示:

//add
Sales_data add(const Sales_data &lhs, const Sales_data &rhs)
{
    Sales_data sum = lhs;
    sum.combine(rhs);
    return sum;
}
    
//read
std::istream &read(std::istream &is, Sales_data &item)
{
   is >> item.bookNo >> item.units_sold >> item.sellingprice >> item.saleprice;
   return is;
}
    
//print
std::ostream &print(std::ostream &os, const Sales_data &item)
{
    os << item.isbn() << " " << item.units_sold << "  " << item.sellingprice << "  " << item.saleprice << "  " << item.discount;
    return os;
}

练习7:使用这些新函数重写7.1.2节(第233页)练习中的交易处理程序。

【出题思路】

用read函数替代>>,print函数替代<<,add函数替代combine函数。

【解答】

我们自定义的print函数不负责输出回车符,满足题意的程序如下所示:

#ifndef SALES_DATA_7_7_H
#define SALES_DATA_7_7_H
#include <iostream>
#include <string>


class Sales_data {

// these declarations are explained section 7.2.1, p. 270
// and in chapter 14, pages 557, 558, 561
friend std::istream& operator >> (std::istream&, Sales_data&);
friend std::ostream& operator << (std::ostream&, const Sales_data&);
friend bool operator < (const Sales_data&, const Sales_data&);
friend bool operator == (const Sales_data&, const Sales_data&);
//add
friend Sales_data add(const Sales_data &lhs, const Sales_data &rhs);

//read
friend std::istream &read(std::istream &is, Sales_data &item);

//print
friend std::ostream &print(std::ostream &os, const Sales_data &item);

public:
    // constructors are explained in section 7.1.4, pages 262 - 265
    // default constructor needed to initialize members of built-in type
    Sales_data() = default;
    Sales_data(const std::string &book): bookNo(book) { }
    Sales_data(std::istream &is) { is >> *this; }


//定义公有函数成员
public:
    // operations on Sales_item objects
    // member binary operator: left-hand operand bound to implicit this pointer
    Sales_data& operator += (const Sales_data&);

    // operations on Sales_item objects
    double avg_price() const;
    std::string isbn() const { return bookNo; }
    //combine函数用于把两个ISBN相同的销售记录合并在一起
    Sales_data& combine(const Sales_data &rhs)
    {
        units_sold += rhs.units_sold;                   //累加书籍的销售量
        saleprice = (rhs.saleprice * rhs.units_sold + saleprice * units_sold)
                    / (rhs.units_sold + units_sold);    //重新计算实现销售价格
        discount = saleprice / sellingprice;            //重新计算实际折扣
        revenue += rhs.revenue;                         //总销售额
        return *this;                                   //返回合并后的结果
    }



// 定义公有数据成员,外部普通函数要调用
public:
    std::string bookNo;             // 书籍编号,隐式初始化为空串
    unsigned units_sold = 0;        // 销售量,显式初始化为0
    double sellingprice = 0.0;      // 原始价格,显式初始化为0.0
    double saleprice = 0.0;         // 实售价格,显式初始化为0.0
    double discount = 0.0;          // 折扣,显式初始化为0.0
    double revenue = 0.0;           // 总收入
};


//add
Sales_data add(const Sales_data &lhs, const Sales_data &rhs)
{
    Sales_data sum = lhs;
    sum.combine(rhs);
    return sum;
}


//read
std::istream& read(std::istream &is, Sales_data &item)
{
    is >> item.bookNo >> item.units_sold >> item.sellingprice >> item.saleprice;

    return is;
}


//print
std::ostream& print(std::ostream &os, const Sales_data &item)
{
    os << item.isbn() << " " << item.units_sold << "  " << item.sellingprice << "  " << item.saleprice
    << "  " << item.discount;
    return os;
}

// used in chapter 10
inline bool compareIsbn(const Sales_data &lhs, const Sales_data &rhs)
{
    return lhs.isbn() == rhs.isbn();
}

// nonmember binary operator: must declare a parameter for each operand
Sales_data operator + (const Sales_data&, const Sales_data&);


inline bool operator == (const Sales_data &lhs, const Sales_data &rhs)
{
    // must be made a friend of Sales_item
    return lhs.units_sold == rhs.units_sold &&
           lhs.revenue == rhs.revenue &&
           lhs.isbn() == rhs.isbn();
}


inline bool operator != (const Sales_data &lhs, const Sales_data &rhs)
{
    return !(lhs == rhs); // != defined in terms of operator==
}


// assumes that both objects refer to the same ISBN
Sales_data& Sales_data::operator += (const Sales_data& rhs)
{
    units_sold += rhs.units_sold;
    revenue += rhs.revenue;
    return *this;
}


// assumes that both objects refer to the same ISBN
Sales_data operator + (const Sales_data& lhs, const Sales_data& rhs)
{
    Sales_data ret(lhs);  // copy (|lhs|) into a local object that we'll return
    ret += rhs;           // add in the contents of (|rhs|)
    return ret;           // return (|ret|) by value
}


//接收4个参数分别为:ISBN,销售量,原价,实际售价
std::istream& operator >> (std::istream& in, Sales_data& s)
{


    in >> s.bookNo >> s.units_sold >> s.sellingprice >> s.saleprice;
    // check that the inputs succeeded
    if (in)
        s.revenue = s.units_sold * s.saleprice;
    else
        s = Sales_data();  // input failed: reset object to default state
    return in;
}


std::ostream& operator << (std::ostream& out, const Sales_data& s)
{
    out << s.isbn() << " " << s.units_sold << " "
        << s.revenue << " " << s.avg_price();
    return out;
}


double Sales_data::avg_price() const
{
    if (units_sold)
        return revenue / units_sold;
    else
        return 0;
}

#endif // SALES_DATA_7_7_H
#include <iostream>
#include "Sales_data_7_7.h"

using namespace std;

int main()
{
    cout << "请输入交易记录(ISBN),销售量,原价,实际售价:" << endl;
    Sales_data total;                   //保存下一条交易记录的变量
    //读入第一条交易记录,并确保有数据可以处理
    if(read(cin, total))
    {
        Sales_data trans;               //保存和的变量
        //读入并处理剩余交易记录
        while(read(cin, trans))
        {
            //如果我们仍在处理相同的书
            if(total.isbn() == trans.isbn())
            {
                total = add(total, trans);   //更新总销额
            }
            else
            {
                //打印前一本书的结果
                print(cout, total);
                cout  << endl;
                total = trans;          //total现在表示下一本书的销售额
            }
        }
        print(cout, total);
        cout  << endl;          //打印最后本书的结果
    }
    else
    {
        //没有输入! 警告读者
        cerr << "No data?!" << endl;
        return -1;                      //表示失败
    }

    return 0;
}

运行结果:

 

练习8:为什么read函数将其Sales_data参数定义成普通的引用,而print将参定义成常量引用?
【出题思路】
本题考查理解Sales_data类中输入输出函数的原理。
【解答】
read函数将其Sales_data参数定义成普通的引用是因为我们需要从标准输入流中读取数据并将其写到给定的Sales_data对象,因此需要有修改对象的权限。而print将其参数定义成常量引用是因为它只负责数据的输出,不对其做任何更改。

练习9:对于7.1.2节(第233页)练习中的代码,添加读取和打印Person对象的操作。
【出题思路】
仿照Sales_data类,为Person类添加相应的read和print函数。
【解答】
满足题意的read和print函数如下所示:

#include <iostream>
#include <string>

using namespace std;

class Person
{
public:
    string getName() const {return strName; }
    string getAddress() const {return strAddress; }
public:
    string strName;
    string strAddress;

};

std::istream &read(std::istream &is, Person &per)
{
    is >> per.strName >> per.strAddress;
    return is;
}

std::ostream &print(std::ostream &os, const Person &per)
{
    os << "name: " << per.getName() << "       address: " <<  per.getAddress() << endl;
    return os;
}

int main()
{
    Person person;

    if(read(cin, person))
    {
        print(cout, person);
    }
    else
    {
        //没有输入! 警告读者
        cerr << "No data?!" << endl;
        return -1;//表示失败
    }

    return 0;
}

运行结果:

 

练习10:在下面这条if语句中,条件部分的作用是什么?
                if(read(read(cin, data1)data2))
【出题思路】
read函数的返回类型是std::istream &,体会这里使用引用的作用。
【解答】
因为read函灵敏的返回类型是引用,所以read(cin, data)的返回值可以继续作为外层read函数的实参使用。该条件检验读入data1和data2的过程是否正确,如果正确,条件满足,否则条件不满足。

练习11:在你的Sales_data类中添加构造函数,然后编写一段程序令其用到每个构造函数。
【出题思路】
在不同情况下,初始化Sales_data对象所需的数据有所不同,分别为其设计构造函数,同时也利用C++11新标准提供的=default定义默认构造函数。
【解答】
满足题意的4个构造函数分别如下所示:

#ifndef SALES_DATA_7_11_H
#define SALES_DATA_7_11_H


#include <iostream>
#include <string>

using namespace std;

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

public:
    Sales_data() = default;
    Sales_data(const std::string &book): bookNo(book) {}
    Sales_data(const std::string &book, const unsigned num,
               const double sellp, const double salep);
    Sales_data(std::istream &is);

    double avg_price() const;
    std::string isbn() const { return bookNo; }

private:
    std::string bookNo;             //书籍编号,隐式初始化为空串
    unsigned units_sold = 0;        //销售量,显示初始化为0
    double sellingprice = 0.0;      //原始价格,显示初始化为0.0
    double saleprice = 0.0;         //实售价格,显式初始化为0.0
    double discount = 0.0;          //折扣,显式初始化为0.0
    double revenue = 0.0;           // 总收入
};

//接收4个参数分别为:ISBN,销售量,原价,实际售价
std::istream& operator >> (std::istream& in, Sales_data& s)
{

    in >> s.bookNo >> s.units_sold >> s.sellingprice >> s.saleprice;
    // check that the inputs succeeded
    if (in)
        s.revenue = s.units_sold * s.saleprice;
    else
        s = Sales_data();  // input failed: reset object to default state
    return in;
}


std::ostream& operator << (std::ostream& out, const Sales_data& s)
{
    out << s.isbn() << " " << s.units_sold << " "
        << s.revenue << " " << s.avg_price();
    return out;
}

double Sales_data::avg_price() const
{
    if (units_sold)
        return revenue / units_sold;
    else
        return 0;
}

Sales_data::Sales_data(const std::string &book, const unsigned num,
                       const double sellp, const double salep)
{
    bookNo = book;
    units_sold = num;
    sellingprice = sellp;
    if(sellingprice != 0)
        discount = saleprice / sellingprice;
}

Sales_data::Sales_data(std::istream &is)
{
    is >> *this;
}

#endif // SALES_DATA_7_11_H
#include <iostream>
#include "Sales_data_7_11.h"
#include <string>
using namespace std;

int main()
{
    cout << "构造函数调用:" << endl;
    Sales_data data1;
    Sales_data data2("978-7-121-15535-2");
    Sales_data data3("978-7-121-15535-2", 100, 128, 109);
    Sales_data data4(cin);

    cout << "书籍的销售情况是:" << endl;
    cout << "data1:" << data1 << endl;
    cout << "data2:" << data2 << endl;
    cout << "data3:" << data3 << endl;
    cout << "data4:" << data4 << endl;

    return 0;
}

运行结果:

 

练习12:把只接受一个istream作为能数的构造函数定义移到类的内部。

【出题思路】

构造函数既可以定义在类的外部,也可以定义在类的内部。

【解答】

按照题目要求,把只接受一个istream作为参数的构造函数定义到类的内部之后,类的形式如下所示:

#include <iostream>
#include <string>

using namespace std;

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

public:
    Sales_data() = default;
    Sales_data(const std::string &book): bookNo(book) {}
    Sales_data(const std::string &book, const unsigned num,
               const double sellp, const double salep);
    Sales_data(std::istream &is) {is >> *this;};//构造函数实现定义到类的类部

    double avg_price() const;
    std::string isbn() const { return bookNo; }

private:
    std::string bookNo;             //书籍编号,隐式初始化为空串
    unsigned units_sold = 0;        //销售量,显示初始化为0
    double sellingprice = 0.0;      //原始价格,显示初始化为0.0
    double saleprice = 0.0;         //实售价格,显式初始化为0.0
    double discount = 0.0;          //折扣,显式初始化为0.0
    double revenue = 0.0;           // 总收入
};

 

练习13:使用istream构造函数重写第229页的程序。

【出题思路】

原来的程序使用Sales_data类的默认构造函数,本题改为使用接受istream的构造函数。

【解答】

改写后的程序如下所示:

#ifndef SALES_DATA_7_13_H
#define SALES_DATA_7_13_H

#include <iostream>
#include <string>


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

public:
    // constructors are explained in section 7.1.4, pages 262 - 265
    // default constructor needed to initialize members of built-in type
    Sales_data() = default;
    Sales_data(const std::string &book): bookNo(book) { }
    Sales_data(std::istream &is);
    Sales_data(const std::string &book, const unsigned num, const double sellp, const double salep);

//定义公有函数成员
public:
    // operations on Sales_item objects
    // member binary operator: left-hand operand bound to implicit this pointer
    Sales_data& operator += (const Sales_data&);

    // operations on Sales_item objects
    double avg_price() const;
    std::string isbn() const { return bookNo; }
    //combine函数用于把两个ISBN相同的销售记录合并在一起
    Sales_data& combine(const Sales_data &rhs)
    {
        units_sold += rhs.units_sold;                   //累加书籍的销售量
        saleprice = (rhs.saleprice * rhs.units_sold + saleprice * units_sold)
                    / (rhs.units_sold + units_sold);    //重新计算实现销售价格
        discount = saleprice / sellingprice;            //重新计算实际折扣
        revenue += rhs.revenue;                         //总销售额
        return *this;                                   //返回合并后的结果
    }


// 定义公有数据成员,外部普通函数要调用
public:
    std::string bookNo;             // 书籍编号,隐式初始化为空串
    unsigned units_sold = 0;        // 销售量,显式初始化为0
    double sellingprice = 0.0;      // 原始价格,显式初始化为0.0
    double saleprice = 0.0;         // 实售价格,显式初始化为0.0
    double discount = 0.0;          // 折扣,显式初始化为0.0
    double revenue = 0.0;           // 总收入
};

Sales_data::Sales_data(const std::string &book, const unsigned num, const double sellp, const double salep)
{
    bookNo = book;
    units_sold = num;
    sellingprice = sellp;
    saleprice = salep;
    if(0 != sellingprice)
    {
        discount = saleprice / sellingprice;   //计算实际折扣
    }
}

Sales_data::Sales_data(std::istream &is)
{
    is >> *this;
}

//add
Sales_data add(const Sales_data &lhs, const Sales_data &rhs)
{
    Sales_data sum = lhs;
    sum.combine(rhs);
    return sum;
}

//read
std::istream& read(std::istream &is, Sales_data &item)
{
    is >> item.bookNo >> item.units_sold >> item.sellingprice >> item.saleprice;

    return is;
}

//print
std::ostream& print(std::ostream &os, const Sales_data &item)
{
    os << item.isbn() << " " << item.units_sold << "  " << item.sellingprice << "  " << item.saleprice
    << "  " << item.discount;
    return os;
}

// used in chapter 10
inline bool compareIsbn(const Sales_data &lhs, const Sales_data &rhs)
{
    return lhs.isbn() == rhs.isbn();
}

// nonmember binary operator: must declare a parameter for each operand
Sales_data operator + (const Sales_data&, const Sales_data&);

inline bool operator == (const Sales_data &lhs, const Sales_data &rhs)
{
    // must be made a friend of Sales_item
    return lhs.units_sold == rhs.units_sold &&
           lhs.revenue == rhs.revenue &&
           lhs.isbn() == rhs.isbn();
}

inline bool operator != (const Sales_data &lhs, const Sales_data &rhs)
{
    return !(lhs == rhs); // != defined in terms of operator==
}

// assumes that both objects refer to the same ISBN
Sales_data& Sales_data::operator += (const Sales_data& rhs)
{
    units_sold += rhs.units_sold;
    revenue += rhs.revenue;
    return *this;
}

// assumes that both objects refer to the same ISBN
Sales_data operator + (const Sales_data& lhs, const Sales_data& rhs)
{
    Sales_data ret(lhs);  // copy (|lhs|) into a local object that we'll return
    ret += rhs;           // add in the contents of (|rhs|)
    return ret;           // return (|ret|) by value
}

//接收4个参数分别为:ISBN,销售量,原价,实际售价
std::istream& operator >> (std::istream& in, Sales_data& s)
{
    in >> s.bookNo >> s.units_sold >> s.sellingprice >> s.saleprice;
    // check that the inputs succeeded
    if (in)
        s.revenue = s.units_sold * s.saleprice;
    else
        s = Sales_data();  // input failed: reset object to default state
    return in;
}

std::ostream& operator << (std::ostream& out, const Sales_data& s)
{
    out << s.isbn() << " " << s.units_sold << " "
        << s.revenue << " " << s.avg_price();
    return out;
}

double Sales_data::avg_price() const
{
    if (units_sold)
        return revenue / units_sold;
    else
        return 0;
}

#endif // SALES_DATA_7_13_H
#include <iostream>
#include "Sales_data_7_13.h"

using namespace std;

int main()
{
    cout << "请输入交易记录(ISBN),销售量,原价,实际售价:" << endl;
    Sales_data total(cin);                      //保存当前求和结果的变量

    if(cin)
    {                                           //读入第一笔交易记录
        Sales_data trans(cin);                  //保存和的变量
        do                                      //保存下一条交易数据的变量
        {
                                                //读放剩余的交易
            if(total.isbn() == trans.isbn())    //检查isbn
            {
                total.combine(trans);           //更新变量total当前的值
            }
            else
            {
                print(cout, total) << endl;     //输出结果
                total = trans;                  //处理下一本书
            }
        }while(read(cin, trans));

        print(cout, total) << endl;             //输出最后一条交易
    }
    else
    {
        cerr << "No data?!" << endl;            //没有输入! 警告读者
        return -1;                              //表示失败
    }

    return 0;
}

 运行结果:

 

练习7.14:编写一个构造函数,令其用我们提供的类内初始值显式地初始化成员。
【出题思路】
构造函数初始值列表负责为新创建的对象的一个或几个数据成员赋初值。构造函数初始值是成员名字的一个列表,每个名字后面紧跟括号括起来的成员初始值,不同成员的初始化通过逗号分隔开。
【解答】
使用初始值列表的构造函数是:
Sales_data(const std::string &book)
            :bookNo(book), units_sold(0), sellingprice(0), saleprice(0), discount(0) { }

练习7.15:为你的Person类添加正确的构造函数。
【出题思路】
仿照Sales_data类,为Person类添加默认构造函数、接受两个实参的构造函数和从标准输入流中读取数据的构造函数。
【解答】
添加上述3个构造函数之后,新的Person类如下所示:

#include <iostream>
#include <string>
using namespace std;


class Person
{
    friend std::istream& operator >> (std::istream&, Person&);

private:
    string strName;             //姓名
    string strAddress;          //地址

public:
    Person() = default;

    Person(const string &name, const string &add)
    {
        strName = name;
        strAddress = add;
    }

    Person(std::istream &input)  { input >> *this; }

    string getName() const
    {
        return strName;         //返回姓名
    }

    string getAddress() const
    {
        return strAddress;      //返回地址
    }

};

std::istream& operator >> (std::istream& in, Person& per)
{
    in >> per.strName >> per.strAddress;
    return in;
}

int main()
{
    cout << "请输入姓名和地址:" << endl;
    Person person(cin);
    cout << "name: " << person.getName() << "       address: " << person.getAddress() << endl;

    return 0;
}

运行结果:

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值