练习7.21 修改你的Sales_data 类使其隐藏实现的细节。你之前编写的关于Sales_data操作的程序应该继续使用,借助类的新定义重新编译该程序,确保其正常工作。
//Sale_data.h
#pragma once
#include <iostream>
#include <string>
using namespace std;
struct Sales_data {
friend istream &read(istream &is, Sales_data &item);
friend ostream &print(ostream &os, const Sales_data &item);
friend Sales_data add(const Sales_data &a, const Sales_data &b);
public:
Sales_data() {};
Sales_data(const string &s): bookNo(s){}
Sales_data(const string &s, const unsigned cnt, const double p):
bookNo(s), units_sold(cnt), revenue(cnt*p){}
Sales_data(istream &is) {
double price;
is >> this->bookNo >> this->units_sold >> price;
this->revenue = this->units_sold * price;
}
string isbn()const { return this->bookNo; }
Sales_data &combine(const Sales_data &other);
private:
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
istream &read(istream &is, Sales_data &item);
ostream &print(ostream &os, const Sales_data &item);
Sales_data add(const Sales_data &a, const Sales_data &b);
//Sale_data.cpp
#include"Sale_data.h"
//Sales_data::Sales_data(istream &is) {
// read(is, *this);
//}
Sales_data &Sales_data::combine(const Sales_data &other) {
this->units_sold += other.units_sold;
this->revenue += other.revenue;
return *this;
}
istream &read(istream &is, Sales_data &item) {
double price;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = item.units_sold * price;
return is;
}
ostream &print(ostream &os, const Sales_data &item) {
os << item.bookNo << " " << item.units_sold << " " << item.revenue;
return os;
}
Sales_data add(const Sales_data &a, const Sales_data &b) {
Sales_data sum = a;
sum.combine(b);
return sum;
}
//main.cpp
#include"Sale_data.h"
int main(int argc, char **argv)
{
Sales_data total(cin);
if (!total.isbn().empty()) {
while (true) {
Sales_data trans(cin);
if (trans.isbn().empty()) break;
if (total.isbn() == trans.isbn()) {
total.combine(trans);
}
else {
print(cout, total) << endl;
total = trans;
}
}
print(cout, total) << endl;
}
else {
cerr << "No Data ?!" << endl;
}
return 0;
}
练习7.22 修改你的Person 类使其隐藏实现的细节。
#include <iostream>
#include <string>
using namespace std;
struct Person;
istream &read(std::istream&, Person&);
class Person {
friend auto read(istream &is, Person &one)->istream &;
friend auto print(ostream &os, const Person &one)->ostream &;
public:
Person() = default;
Person(const string &sname, const string &saddr) :name(sname), address(saddr) {}
Person(istream &is) { read(is, *this); }
auto getName()const -> const string & { return this->name; }
auto getAddr()const -> const string & { return this->addr; }
private:
string name;
string addr;
};
auto read(istream &is, Person &one)->istream & {
is >> one.name >> one.addr;
return is;
}
auto print(ostream &os, const Person &one)->ostream & {
os << one.name << " " << one.addr;
return os;
}
int main(int argc, char **argv)
{
return 0;
}
练习7.23 编写你自己的Screen 类型。
练习7.24 给你的Screen 类添加三个构造函数:一个默认构造函数;另一个构造函数接受宽和高的值,然后将contents 初始化成给定数量的空白;第三个构造函数接受宽和高的值以及一个字符,该字符作为初始化后屏幕的内容。
//Screen.h
#pragma once
#include <iostream>
#include <string>
using namespace std;
class Screen {
public:
using pos = std::string::size_type;
Screen() = default;
Screen(const pos h, const pos w) :
height(h), width(w), contents(h*w, ' ') {}
Screen(const pos h, const pos w, const char c):
height(h), width(w), contents(h*w, c) {}
char get() const {return contents[cursor];}
char get(pos r, pos c) const { return contents[width*r+c]; }
Screen &move(pos r, pos c) { cursor = r * width + c; return *this; }
private:
pos cursor = 0;
pos height = 0, width = 0;
string contents;
};
练习7.25 Screen
能安全地依赖于拷贝和赋值操作的默认版本吗?如果能,为什么?如果不能?为什么?
我认为是可以的,因为Screen主要是用了内置类型,它们本身都有完善的拷贝和赋值操作。但如果设计管理动态内存的类则不能依赖于拷贝和赋值操作的默认版本,而且也应该尽量使用string 和 vector 来避免动态管理内存的复杂性。
**练习7.26 将Sales_data::avg_price
定义成内联函数。 **
//Sale_data.h
#pragma once
#include <iostream>
#include <string>
using namespace std;
struct Sales_data {
friend istream &read(istream &is, Sales_data &item);
friend ostream &print(ostream &os, const Sales_data &item);
friend Sales_data add(const Sales_data &a, const Sales_data &b);
public:
Sales_data() {};
Sales_data(const string &s): bookNo(s){}
Sales_data(const string &s, const unsigned cnt, const double p):
bookNo(s), units_sold(cnt), revenue(cnt*p){}
Sales_data(istream &is) {
double price;
is >> this->bookNo >> this->units_sold >> price;
this->revenue = this->units_sold * price;
}
string isbn()const { return this->bookNo; }
Sales_data &combine(const Sales_data &other);
private:
inline double avg_price() const;
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
inline
double Sales_data::avg_price() const
{
return units_sold ? revenue / units_sold : 0;
}
istream &read(istream &is, Sales_data &item);
ostream &print(ostream &os, const Sales_data &item);
Sales_data add(const Sales_data &a, const Sales_data &b);
练习7.28 给你自己的Screen 类添加move、set 和display 函数,通过执行下面的代码检验你的类是否正确。
Screen myScreen(5, 5, 'X');
myScreen.move(4, 0).set('#').display(cout);
cout << "\n";
myScreen.display(cout);
cout << "\n";
//Screen.h
#pragma once
#include <iostream>
#include <string>
using namespace std;
class Screen {
public:
using pos = std::string::size_type;
Screen() = default;
Screen(const pos h, const pos w) :
height(h), width(w), contents(h*w, ' ') {}
Screen(const pos h, const pos w, const char c):
height(h), width(w), contents(h*w, c) {}
char get() const {return contents[cursor];}
char get(pos r, pos c) const { return contents[width*r+c]; }
Screen &move(pos r, pos c) { cursor = r * width + c; return *this; }
Screen &set(const char c) { contents[cursor] = c; return *this; }
Screen &set(pos r, pos col, const char ch) { contents[r*width + col] = ch; return *this; }
Screen &display(ostream &os) { do_display(os); return *this; }
const Screen &display(ostream &os) const { do_display(os); return *this; }
private:
void do_display(ostream &os)const { os << contents; }
pos cursor = 0;
pos height = 0, width = 0;
string contents;
};
main.cpp
#include "Screen.h"
int main(int argc, char **argv)
{
Screen myScreen(5, 5, 'X');
myScreen.move(4, 0).set('#').display(cout);
cout << "\n";
myScreen.display(cout);
cout << "\n";
return 0;
}
练习7.28 如果move、set和display函数的返回类型不是Screen& 而是Screen,则在上一个练习中奖会发生什么?
会发生第一行的(4,0)位置被改变成’#’,而第二行没有。
练习7.29 修改你的Screen 类,令move、set和display函数返回Screen并检查程序的运行结果,在上一个练习中你的推测正确吗?
正确
//返回 Screen& 的结果
XXXXXXXXXXXXXXXXXXXX#XXXX
XXXXXXXXXXXXXXXXXXXX#XXXX
^ <- 这里
//返回 Screen 的结果
XXXXXXXXXXXXXXXXXXXX#XXXX
XXXXXXXXXXXXXXXXXXXXXXXXX
^ <- 这里
练习7.30 通过this指针使用成员的做法虽然合法,但是有点多余。讨论显示使用指针访问成员的优缺点。
优点:
- 代码更易读。
- 发生变量重名时用于区别变量。
缺点:
- 冗余