修改你的Sales_data 类使其隐藏实现的细节。你之前编写的关于Sales_data操作的程序应该继续使用,借助类的新定义重新编译该程序,确保其正常工作。
#ifndef CP5_ex7_21_h
#define CP5_ex7_21_h
#include
#include
class Sales_data
{
friend std::istream &read(std::istream &is, Sales_data &item);
friend std::ostream &print(std::ostream &os, const Sales_data &item);
friend Sales_data add(const Sales_data &lhs, const Sales_data &rhs);
public:
Sales_data() = default;
Sales_data(const std::string &s) :bookNo(s) {}
Sales_data(const std::string &s, unsigned n, double p) :bookNo(s), units_sold(n), revenue(n*p) {}
Sales_data(std::istream &is) { read(is, *this); }
std::string isbn() const { return bookNo; };
Sales_data& combine(const Sales_data&);
private:
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
std::istream &read(std::istream &is, Sales_data &item)
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}
std::ostream &print(std::ostream &os, const Sales_data &item)
{
os << item.isbn() << " " << item.units_sold << " " << item.revenue;
return os;
}
Sales_data add(const Sales_data &lhs, const Sales_data &rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
#endif
修改你的Person 类使其隐藏实现的细节。
#ifndef CP5_ex7_22_h
#define CP5_ex7_22_h
#include
#include
struct Person
{
friend std::istream &read(std::istream &is, Person &person);
friend std::ostream &print(std::ostream &os, const Person &person);
public:
Person() = default;
Person(const std::string sname, const std::string saddr) :name(sname), address(saddr) {}
Person(std::istream &is) { read(is, *this); }
std::string getName() const { return name; }
std::string getAddress() const { return address; }
private:
std::string name;
std::string address;
};
std::istream &read(std::istream &is, Person &person)
{
is >> person.name >> person.address;
return is;
}
std::ostream &print(std::ostream &os, const Person &person)
{
os << person.name << " " << person.address;
return os;
}
#endif
编写你自己的Screen 类型。
#ifndef CP5_ex7_23_h
#define CP5_ex7_23_h
#include
class Screen
{
public:
using pos = std::string::size_type;
Screen() = default;
Screen(pos ht, pos wd, char c) :height(ht), width(wd), contents(ht*wd, c)
{}
char get() const { return contents[cursor]; }
char get(pos r, pos c) const { return contents[r*width + c]; }
private:
pos cursor = 0;
pos height = 0;
pos width = 0;
std::string contents;
};
#endif
给你的Screen 类添加三个构造函数:一个默认构造函数;另一个构造函数接受宽和高的值,然后将contents 初始化成给定数量的空白;第三个构造函数接受宽和高的值以及一个字符,该字符作为初始化后屏幕的内容。
#ifndef CP5_ex7_24_h
#define CP5_ex7_24_h
#include
class Screen
{
public:
using pos = std::string::size_type;
Screen() = default;
Screen(pos ht, pos wd) :height(ht), width(wd), contents(ht*wd, ' ') {}
Screen(pos ht, pos wd, char c) :height(ht), width(wd), contents(ht*wd, c) {}
char get() const { return contents[cursor]; }
char get(pos r, pos c) const { return contents[r*width + c]; }
private:
pos cursor = 0;
pos height = 0;
pos width = 0;
std::string contents;
};
#endif
Screen 能安全地依赖于拷贝和赋值操作的默认版本吗?如果能,为什么?如果不能?为什么?
能。Screen 的成员只有内置类型和 string,因此能安全地依赖于拷贝和赋值操作的默认版本。管理动态内存的类则不能依赖于拷贝和赋值操作的默认版本,而且也应该尽量使用string 和 vector 来避免动态管理内存的复杂性。
将Sales_data::avg_price 定义成内联函数。
#ifndef CP5_ex7_26_h
#define CP5_ex7_26_h
#include
#include
class Sales_data
{
friend std::istream &read(std::istream &is, Sales_data &item);
friend std::ostream &print(std::ostream &os, const Sales_data &item);
friend Sales_data add(const Sales_data &lhs, const Sales_data &rhs);
public:
Sales_data() = default;
Sales_data(const std::string &s) :bookNo(s) {}
Sales_data(const std::string &s, unsigned n, double p) :bookNo(s), units_sold(n), revenue(n*p) {}
Sales_data(std::istream &is) { read(is, *this); }
std::string isbn() const { return bookNo; };
Sales_data& combine(const Sales_data&);
private:
inline double avg_price() const;
private:
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
inline
double Sales_data::avg_price() const
{
return units_sold ? revenue / units_sold : 0;
}
std::istream &read(std::istream &is, Sales_data &item);
std::ostream &print(std::ostream &os, const Sales_data &item);
Sales_data add(const Sales_data &lhs, const Sales_data &rhs);
#endif
给你自己的Screen 类添加move、set 和display 函数,通过执行下面的代码检验你的类是否正确。
Screen myScreen(5, 5, 'X');
myScreen.move(4, 0).set('#').display(cout);
cout << "\n";
myScreen.display(cout);
cout << "\n";
#ifndef CP5_ex7_27_h
#define CP5_ex7_27_h
#include
#include
class Screen
{
public:
using pos = std::string::size_type;
Screen() = default; // 1
Screen(pos ht, pos wd) :height(ht), width(wd), contents(ht*wd, ' ') {} // 2
Screen(pos ht, pos wd, char c) :height(ht), width(wd), contents(ht*wd, c) {} // 3
char get() const { return contents[cursor]; }
char get(pos r, pos c) const { return contents[r*width + c]; }
inline Screen& move(pos r, pos c);
inline Screen& set(char c);
inline Screen& set(pos r, pos c, char ch);
const Screen& display(std::ostream &os) const { do_display(os); return *this; }
Screen& display(std::ostream &os) { do_display(os); return *this; }
private:
void do_display(std::ostream &os) const { os << contents; }
private:
pos cursor = 0;
pos height = 0, width = 0;
std::string contents;
};
inline Screen& Screen::move(pos r, pos c)
{
cursor = r*width + c;
return *this;
}
inline Screen& Screen::set(char c)
{
contents[cursor] = c;
return *this;
}
inline Screen& Screen::set(pos r, pos c, char ch)
{
contents[r*width + c] = ch;
return *this;
}
#endif
如果move、set和display函数的返回类型不是Screen& 而是Screen,则在上一个练习中将会发生什么?
如果返回类型是Screen,那么move返回的是 *this 的一个副本,因此set函数只能改变临时副本而不能改变myScreen的值。
修改你的Screen 类,令move、set和display函数返回Screen并检查程序的运行结果,在上一个练习中你的推测正确吗?
推测正确。
通过this指针使用成员的做法虽然合法,但是有点多余。讨论显示使用指针访问成员的优缺点。
优点:
程序的意图更明确
函数的参数可以与成员同名,如
void setAddr(const std::string &addr) { this->addr = addr; }
缺点:
有时候显得有点多余,如
std::string getAddr() const { return this->addr; }