题目中所要求的所有类的要求如下:
Person.h
#ifndef _PERSON_H
#define _PERSON_H
#include <iostream>
#include <string>
class Person
{
public:
Person(std::string name, std::string address) : name(name), address(address) {};
~Person() {};
std::string nameInfo() { return name; }
std::string addressInfo() { return address; }
public:
std::string name;
std::string address;
};
std::istream &recordInfno(std::istream&, Person&);
std::ostream &outputInfno(std::ostream&,const Person&);
#endif
Person.c
#include "Person.h"
using namespace std;
std::istream &recordInfno(std::istream& is, Person& personInfo)
{
is >> personInfo.name >> personInfo.address;
return is;
}
std::ostream &outputInfno(std::ostream& os,const Person& personInfo)
{
os << personInfo.name << " " << personInfo.address << endl;
return os;
}
Sales_data.h
#ifndef _SALES_DATA_H
#define _SALES_DATA_H
#include <string>
/*
定义一个实现书店交易的类
它要实现表示一本书的总销售额、售出册数、平均售价
那么要是实现这个类,得先定义其数据成员,然后是类所能提供得接口(实现类所需要的操作)
然后开始实现接口
*/
class Sales_data
{
public:
Sales_data(std::string number) :bookNo(number), units_sold(0), revenue(0.0){};
Sales_data(std::istream &is) { read(is, *this); };
Sales_data() {};
~Sales_data() {};
std::string isbn() const { return bookNo; }//返回ISBN编号
Sales_data& combine(const Sales_data &); //将一个Sales_date对象加到另一个对象上去
double avg_price() const; //计算均价
private:
std::string bookNo; //ISBN编号
unsigned units_sold ;//销售数量
double revenue ; //销售总额
friend Sales_data add(const Sales_data&, const Sales_data&); //执行两个Sales_date对象的加法
friend std::ostream &print(std::ostream&, const Sales_data&);//将Sales_date对象输出到ostream上
friend std::istream &read(std::istream&, Sales_data&); //从ostream上读入到Sales_date对象
};
//Sale_date 的非成员接口函数
Sales_data add(const Sales_data&, const Sales_data&); //执行两个Sales_date对象的加法
std::ostream &print(std::ostream&, const Sales_data&);//将Sales_date对象输出到ostream上
std::istream &read(std::istream&, Sales_data&); //从ostream上读入到Sales_date对象
#endif
Sales_data.c
#include "Sales_data.h"
#include <iostream>
using namespace std;
istream &read(std::istream& is, Sales_data& rhs)//输入书的编号 数量 价格
{
double price = 0;
is >> rhs.bookNo >> rhs.units_sold >> price;
rhs.revenue += price * rhs.units_sold;
return is;
}
ostream &print(std::ostream &os, const Sales_data &rhs)//输出书的编号 数量 总数 平均价格
{
os << rhs.isbn() << " " << rhs.units_sold << " "
<< rhs.revenue << " " << rhs.avg_price();
return os;
}
inline double Sales_data::avg_price() const //求平均价格
{
if (units_sold)
return revenue / units_sold;
else
return 0;
}
Sales_data& Sales_data::combine(const Sales_data &rhs)
{
units_sold += rhs.units_sold;//把rhs的成员加到this对象的成员上去
revenue += rhs.revenue;
return *this; //返回调用该函数的对象
}
Sales_data add(const Sales_data &lhs, const Sales_data &rhs)
{
Sales_data sum = lhs;
sum.combine(rhs); //把rhs的数据成员加到sum当中
return sum;
}
//int main(void)
//{
// Sales_data total; //保存当前求和的结果的变量
// if (read(cin, total)) //读入第一笔交易到total
// {
// Sales_data trans;
// while (read(cin, trans))//读入新的交易到trans
// {
// if (total.isbn() == trans.isbn())//如果新读入书编号和前面的相同
// total.combine(trans); //执行相加操作
// else
// {
// print(cout, total) << endl; //不相同
// total = trans; //处理下一本书
// }
// }
// print(cout, total) << endl; //输出最后的交易
// }
// else //没有任何交易
// {
// cout << "No data?!" << endl; //通知用户
// }
// system("pause");
// return 0;
//}
Screen.c
#ifndef _SCREEN_H
#define _SCREEN_H
#include <iostream>
#include <string>
class Screen
{
public:
typedef std::string::size_type pos;
Screen() = default; //因为Sreen有另一个构造函数
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) {};
~Screen() {};
char get() const
{
return contents[cursor];
}
char get(pos ht, pos wd) const;
Screen &move(pos , pos );
Screen &set(char);
Screen &set(pos, pos, char);
Screen &display(std::ostream &os)
{
do_display(os); return *this;
}
const Screen &display(std::ostream &os) const
{
do_display(os); return *this;
}
pos size() const;
private:
void do_display(std::ostream &os) const
{
for (size_t i = 0; i < height; i++)
{
for (size_t j = 0; j < width; j++)
{
os << contents[i*width + j];
}
os << "\n";
}
}
private:
pos cursor = 0;
pos height = 0, width = 1;
std::string contents;
};
class Employee
{
public:
Employee() = default;
Employee(std::string num, std::string name, std::string position) :
empNum(num), empName(name), empPosition(name) {};
private:
std::string empNum; //工号
std::string empName; //名字
std::string empPosition; //职位
};
#endif
Screen.c
#include "Screen.h"
using namespace std;
Screen &Screen::move(pos r, pos c)
{
pos row = r*width; //计算行的高度
cursor = row + c; //在行内将光标移到指定的列
return *this; //以左值的形式返回对象
}
char Screen::get(pos r, pos c) const
{
pos row = r * width; //计算的位置
return contents[row + c]; //返回给定列的字符
}
Screen &Screen::set(char c)
{
contents[cursor] = c; //设置当前光标所在位置的新值
return *this;
}
Screen &Screen::set(pos r, pos col, char ch)
{
contents[ r * width + col] = ch; //设置当前光标所在位置的新值
return *this;
}
Screen::pos Screen::size() const
{
return height * width;
}
main.c
#include <iostream>
#include "Sales_data.h"
#include "Person.h"
#include "Screen.h"
#include <vector>
using namespace std;
int main(void)
{
system("pause");
return 0;
}
习题7.1 - 7.3
Sales_data
习题7.4
Person
习题7.5
不应该,名字和住址都可以改
习题7.6 -7.7
Sales_data
习题7.8
read中Sales_data需要被改变,而print中Sales_data不需要
习题7.9
Person
习题7.10
将data1和data2放入同一个缓冲区。
习题7.11 - 7.14
Sales_data
习题7.15
Person
习题7.16
访问说明符出现的位置和次数均无限制,public之后定义的可以被
在整个程序内都可以被访问。而private之后定义的,仅有类内成员访问、
类的友元可以访问
习题7.17
出于统一编程风格的考虑,当我们希望类的所有成员是public时
使用struct,反之,希望成员时private,使用class
以上是书中解释,本人更倾向成员是数据集的时候使用struct,
当成员出现功能性的集合,需要复杂性质的个体操作时使用class。
练习7.18
(1)实现了接口和实现的分离,对类外隐藏实现细节。
(2)确保用户状态不会无意破坏封装对象的状态
(3)被封装的类的具体细节不可改变,而无须调整用户级代码
练习7.19
接口public,数据private
练习7.20
在用户级函数需要访问类内的private成员时声明该函数为友元函数。
友元在使用时可以非常方便的访问非公有成员,但在一定程度上
破了类的封装。
练习7.21
略
练习7.22
略
7.25
可以,本类只有简单的数据类型,可以依靠拷贝和赋值操作的默认版本
练习7.28
在函数定义处,加上inline关键字
练习7.27
Screen myscreen(5, 5, 'x');
myscreen.move(4, 0).set('#').display(cout);
cout << "\n";
myscreen.display(cout);
cout << "\n";
练习7.28 - 7.29
move返回的不是左值,会导致myscreen实际本身不会被改变。
再一次输出时会发现‘#’未被设置成功。
练习7.30
优点在于明确,假设类外或者成员幻术形参有与类内相同的名字,使用
this明确的指出用的是哪一个。
练习7.31
class y;
class x
{
y *point_y;
};
class y
{
x object_x;
};
注:class y需要前向声明,如果没有声明,y是未知类型。
练习7.32
略
练习7.33
Screen::pos Screen::size() const
{
return height * width;
}
Screen.cpp里没有定义pos,用户代码需要引用pos,需要Screen::pos 。
练习7.34
pos类型前面的引用将不合法。
练习7.35
类外的使用的是string,类内使用的是double。类内的type将类外的
type隐蔽了。
有一个错误改正为
Exercise::Type Exercise::setVal(Type parm){
...
}
练习7.36
类内初始化是按照定义顺序来的,按照书上写的话
实际上先初始化rem,然而base此时是未初始化的。rem的值将是未定义的
纠正
struct X {
X (int i, int j): base(i), rem(base % j) { }
int base, rem;
};
练习7.37
略
练习7.38
Sales_data(std::istream &is = std::cin) {read(is, *this);}
练习7.39
不合法,Sales_data()将不知道选择那个版本的构造。
练习7.40
class Employee
{
public:
Employee() = default;
Employee(std::string num, std::string name, std::string position) :
empNum(num), empName(name), empPosition(name) {};
private:
std::string empNum; //工号
std::string empName; //名字
std::string empPosition; //职位
};
练习7.41
委托构造函数本质上是构造函数,只不过它在初始化的时候会使用
本类的另外一个构造函数,即实际上初始化工作是被使用的构造函数
完成的。
练习7.42
class Employee
{
public:
Employee() = default;
Employee(std::string num, std::string name, std::string position) :
empNum(num), empName(name), empPosition(name) {};
Employee(std::string name) :Employee(“0”, name, “ ”) :
private:
std::string empNum; //工号
std::string empName; //名字
std::string empPosition; //职位
};
练习7.43
class NoDefault {
public:
NoDefault(int i) {}
};
class C {
public:
C() : def(0) {} ;
private:
NoDefault def;
};
练习7.44
不合法,类NoDefault 找不到何时的构造函数初始化。
fix : vector<NoDefault> vec(10,1);
练习7.45
合法,C有默认实参
练习7.46
(a)错,有default
(b)错
(c)错
(d)错,简单类型可以,复杂的不行,例如一个类含有另一个类的实体。
练习4.47
不该,使用explicit只能直接初始化,不能隐式转换。
这样做
优点:操作方便,使用”“就可以完成初始化
练习4.48 练习4.49 练习4.50
略
练习4.51
我们通常会用const char* 初始化string,因此不设置explicit
练习4.52
略
练习7.53
class Debug {
public:
constexpr Debug(bool b = true) : rt(b), io(b), other(b) {}
constexpr Debug(bool r, bool i, bool o) : rt(r), io(i), other(0) {}
constexpr bool any() { return rt || io || other;
void set_rt(bool b) { rt = b; }
void set_io(bool b) { io = b; }
void set_other(bool b) { other = b; }
private:
bool rt; // runtime error
bool io; // I/O error
bool other; // the others
};
练习7.54
不应该,它需要返回一个变量值
练习7.55
不是
练习7.56
静态成员使用static在类内声明,但它不属于类的任何一个对象。
它并不是在对象被创建时定义。并且应该在类外初始化和定义
练习7.57
class Account {
public:
void calculate() { amount += amount * interestRate; }
static double rate() { return interestRate; }
static void rate(double newRate) { interestRate = newRate; }
private:
std::string owner;
double amount;
static double interestRate;
static constexpr double todayRate = 42.42;
static double initRate() { return todayRate; }
};
double Account::interestRate = initRate();
练习5.58
class Example {
public:
static constexpr double rate = 6.5;
static constexpr int vecSize = 20;
static vector<double> vec(vecSize );
};