练习7.1
#include <iostream>
#include <string>
using namespace std;
struct Sales_data
{
string bookNo;
unsigned units_sold = 0;
double revenue = 0;
};
int main(int argc, char* argv)
{
Sales_data total;
if (cin >> total.bookNo >> total.units_sold >> total.revenue)
{
Sales_data trans;
while (cin >> trans.bookNo >> trans.units_sold >> trans.revenue)
{
if (total.bookNo == trans.bookNo)
{
total.units_sold += trans.units_sold;
total.revenue += trans.revenue;
}
else
{
cout << total.bookNo << total.units_sold << total.revenue << endl;
total.bookNo = trans.bookNo;
total.units_sold = trans.units_sold;
total.revenue = total.revenue;
}
}
cout << total.bookNo << total.units_sold << total.revenue << endl;
}
else
{
cerr << "No data!" << endl;
return -1;
}
return 0;
}
练习7.2
struct Sales_data
{
string isbn() const { return bookNo; }
Sales_data& combine(const Sales_data& trans);
string bookNo;
unsigned units_sold = 0;
double revenue = 0;
};
Sales_data& Sales_data::combine(const Sales_data& trans)
{
units_sold += trans.units_sold;
revenue += trans.revenue;
return *this;
}
练习7.3
int main(int argc, char* argv)
{
Sales_data total;
if (cin >> total.bookNo >> total.units_sold >> total.revenue)
{
Sales_data trans;
while (cin >> trans.bookNo >> trans.units_sold >> trans.revenue)
{
if (total.isbn() == trans.isbn())
{
total.combine(trans);
}
else
{
cout << total.isbn() << total.units_sold << total.revenue << endl;
total.bookNo = trans.isbn();
total.units_sold = trans.units_sold;
total.revenue = total.revenue;
}
}
cout << total.isbn() << total.units_sold << total.revenue << endl;
}
else
{
cerr << "No data!" << endl;
return -1;
}
return 0;
}
练习7.4
struct Person
{
string name;
string address;
};
练习7.5
struct Person
{
string const getName() const { return name; }
string const getAddr() const { return address; }
string name;
string address;
};
应该是const的,因为这些成员函数的作用只是读取调用它的对象的属性,不需要改变
练习7.6 7.7
#include <iostream>
#include <string>
using namespace std;
struct Sales_data
{
string isbn() const { return bookNo; }
Sales_data& combine(const Sales_data& trans);
string bookNo;
unsigned units_sold = 0;
double revenue = 0;
};
Sales_data& Sales_data::combine(const Sales_data& trans)
{
units_sold += trans.units_sold;
revenue += trans.revenue;
return *this;
}
istream& read(istream& is, Sales_data& item)
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = item.units_sold * price;
return is;
}
ostream& print(ostream& os, Sales_data& item)
{
os << item.isbn() << " " << item.units_sold << " " << item.revenue;
return os;
}
Sales_data add(Sales_data& lhs, Sales_data& rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
int main(int argc, char* argv)
{
Sales_data total;
if (read(cin,total))
{
Sales_data trans;
while (read(cin,trans))
{
if (total.isbn() == trans.isbn())
{
total.combine(trans);
}
else
{
print(cout, total);
cout << endl;
total.bookNo = trans.isbn();
total.units_sold = trans.units_sold;
total.revenue = trans.revenue;
}
}
print(cout, total);
}
else
{
cerr << "No data!" << endl;
return -1;
}
return 0;
}
习题7.8
read参数需要更改对象的属性,因而定义为普通引用;
print无需更改对象属性,定义为常量引用更好些。
习题7.9
#include <iostream>
#include <string>
using namespace std;
struct Person
{
string const getName() const { return name; }
string const getAddr() const { return address; }
string name;
string address;
};
istream& read(istream &is, Person &person)
{
is >> person.name >> person.address;
return is;
}
ostream& print(ostream &os, const Person &person)
{
os << person.name << ' ' << person.address;
return os;
}
int main()
{
Person p1;
read(cin, p1);
print(cout,p1);
return 0;
}
习题7.10
先将输入流中的输入写入data1,再将接下来的输入写入data2.因为is作为参数在函数内部并没有发生改变,read的返回值仍是cin。
习题7.11
#include <iostream>
#include <string>
using namespace std;
struct Sales_data;
istream& read(istream &,Sales_data &);
ostream& print(ostream &,Sales_data &);
struct Sales_data
{
Sales_data() = default;
Sales_data(const string &s):bookNo(s) {}
Sales_data(const string &s, unsigned n, double p):bookNo(s),units_sold(n),revenue(n*p) {}
Sales_data(std::istream &);
string isbn() const { return bookNo; }
Sales_data& combine(const Sales_data& trans);
string bookNo;
unsigned units_sold = 0;
double revenue = 0;
};
Sales_data& Sales_data::combine(const Sales_data& trans)
{
units_sold += trans.units_sold;
revenue += trans.revenue;
return *this;
}
istream& read(istream& is, Sales_data& item)
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = item.units_sold * price;
return is;
}
ostream& print(ostream& os, Sales_data& item)
{
os << item.isbn() << " " << item.units_sold << " " << item.revenue;
return os;
}
Sales_data add(Sales_data& lhs, Sales_data& rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
int main(int argc, char** argv)
{
Sales_data data1;
Sales_data data2("KS990S");
Sales_data data3("KSLLLL",3,10.3);
read(cin,data1);
print(cout,data1);
print(cout,data2);
print(cout,data3);
return 0;
}
习题7.12
#include <iostream>
#include <string>
using namespace std;
struct Sales_data;
istream& read(istream &,Sales_data &);
ostream& print(ostream &,Sales_data &);
struct Sales_data
{
Sales_data() = default;
Sales_data(const string &s):bookNo(s) {}
Sales_data(const string &s, unsigned n, double p):bookNo(s),units_sold(n),revenue(n*p) {}
Sales_data(std::istream &is)
{
read(is,*this);
}
string isbn() const { return bookNo; }
Sales_data& combine(const Sales_data& trans);
string bookNo;
unsigned units_sold = 0;
double revenue = 0;
};
习题7.13
int main(int argc, char** argv)
{
Sales_data total(cin);
if (!total.isbn().empty())
{
Sales_data trans;
while (read(cin,trans))
{
if (total.isbn() == trans.isbn())
{
total.combine(trans);
}
else
{
print(cout, total);
cout << endl;
total = trans;
}
}
print(cout, total);
}
else
{
cerr << "No data!" << endl;
return -1;
}
return 0;
}
练习7.15
#include <iostream>
#include <string>
using namespace std;
struct Person;
istream& read(istream&, Person&);
ostream& print(ostream&,const Person&);
struct Person
{
Person() = default;
Person(const string &nm, const string &addr):name(nm),address(addr) {}
Person(istream &is) {read(is,*this);}
string const getName() const { return name; }
string const getAddr() const { return address; }
string name;
string address;
};
istream& read(istream &is, Person &person)
{
is >> person.name >> person.address;
return is;
}
ostream& print(ostream &os, const Person &person)
{
os << person.name << ' ' << person.address;
return os;
}
int main()
{
Person p1("wfs","dcl"),p2(cin);
print(cout,p1);
print(cout,p2);
return 0;
}
练习7.16
一个类可以包含0个或者多个说明符,对位置和次数都没有限定。public之后的成员在整个程序内都可以被访问,主要用来定义程序的接口;private只能被类的成员函数访问,但是不能被使用该类的代码访问。
练习7.17
有且仅有一个区别:默认访问权限不同
练习7.18
对类的使用者隐藏类的实现细节,这即为封装:可以强制使用者使用类的接口,提升类的标准性与鲁棒性。
练习7.19
理论上是要这样声明的
class Person
{
public:
Person() = default;
Person(const string &nm, const string &addr):name(nm),address(addr) {}
Person(istream &is) {read(is,*this);}
string const getName() const { return name; }
string const getAddr() const { return address; }
private:
string name;
string address;
};
但是这样是无法编译通过的,因为read函数访问了私有成员。所以重写read/print函数。(其实可以声明为友元函数,下一节再说)
istream& read(istream &is, Person &person)
{
string name,addr;
is >> name >> addr;
Person person_tmp(name,addr);
person = person_tmp;
return is;
}
ostream& print(ostream &os, const Person &person)
{
os << person.getName() << ' ' << person.getAddr();
return os;
}
练习7.20
友元是为了允许其他类和函数访问它的非公有成员
利:增加类的灵活性
弊:一定程度上破坏了类的封装性
练习7.21
class Sales_data
{
friend istream& read(istream &,Sales_data &);
friend ostream& print(ostream &,Sales_data &);
public:
Sales_data() = default;
Sales_data(const string &s):bookNo(s) {}
Sales_data(const string &s, unsigned n, double p):bookNo(s),units_sold(n),revenue(n*p) {}
Sales_data(std::istream &is)
{
read(is,*this);
}
string isbn() const { return bookNo; }
Sales_data& combine(const Sales_data& trans);
private:
string bookNo;
unsigned units_sold = 0;
double revenue = 0;
};
练习7.22
class Person
{
friend istream& read(istream&, Person&);
friend ostream& print(ostream&,const Person&);
public:
Person() = default;
Person(const string &nm, const string &addr):name(nm),address(addr) {}
Person(istream &is) {read(is,*this);}
string const getName() const { return name; }
string const getAddr() const { return address; }
private:
string name;
string address;
};
istream& read(istream &is, Person &person)
{
is >> person.name >> person.address;
return is;
}
ostream& print(ostream &os, const Person &person)
{
os << person.name << ' ' << person.address;
return os;
}
练习7.23/24
class Screen
{
public:
using pos = string::size_type;
Screen() = default;
Screen(pos h, pos w):height(h),width(w),content(h*w,' ') {}
Screen(pos h, pos w, char c):height(h),width(w),content(h*w,c) {}
private:
pos height,width;
pos curse=0;
string content;
};
练习7.25
能。Screen的资源都是在内置类型和string类型存储的,可以依赖于合成的版本。
练习7.27
#include <iostream>
#include <string>
using namespace std;
class Screen
{
public:
using pos = string::size_type;
Screen() = default;
Screen(pos h, pos w):height(h),width(w),content(h*w,' ') {}
Screen(pos h, pos w, char c):height(h),width(w),content(h*w,c) {}
char get() const
{
return content[curse];
}
inline char get(pos r, pos c) const ;
Screen &move(pos r, pos c);
Screen &set(pos r, pos c, char ch);
Screen &set(char ch);
const Screen &display(ostream& os) const
{
do_display(os);
return *this;
}
Screen &display(ostream &os)
{
do_display(os);
return *this;
}
private:
void do_display(ostream &os) const
{
os << content;
}
pos height,width;
pos curse=0;
string content;
};
inline
Screen &Screen::move(pos r, pos c)
{
pos row = r*width;
curse = row +c;
return *this;
}
inline
Screen &Screen::set(pos r, pos c, char ch)
{
content[r*width+c] = ch;
return *this;
}
inline
Screen &Screen::set(char ch)
{
content[curse] = ch;
return *this;
}
char Screen::get(pos r, pos c) const
{
return content[r*width+c];
}
练习7.28/29
第一次的输出是(4,0)位置被设置为‘#’的screen内容,而第二次输出则是原始的screen内容。原因在于set与display的对象均为ie产生的副本,myScreen对象并未被set。
练习7.30
转载自https://blog.csdn.net/shamozhizhoutx/article/details/82291127
优点:
更明确,减少误读的可能性;
可以使用名称与成员名相同的形参。
void setAddr(const std::string &addr) { this->addr = addr; }
缺点:
冗余代码增加。
std::string getAddr() const { return this->addr; } // unnecessary
练习7.31
#include <iostream>
using namespace std;
class X;
class Y;
class X
{
Y* y;
};
class Y
{
X x;
};
int main()
{
X test_X;
Y test_Y;
return 0;
}
习题7.32
WIndow_mgr.h
#include <iostream>
#include <vector>
#include <string>
#include "Screen.h"
using namespace std;
class Window_mgr
{
public:
typedef vector<Screen>::size_type ScreenIndex;
void clean(ScreenIndex i);
private:
vector<Screen> screens{Screen(24,24,' ')};
};
void Window_mgr::clean(ScreenIndex i)
{
Screen &s = screens[i];
s.content = string(s.height*s.width,' ');
}
Screen.h
#include <iostream>
#include <string>
using namespace std;
class Screen
{
friend class Window_mgr;
public:
using pos = string::size_type;
Screen() = default;
Screen(pos h, pos w):height(h),width(w),content(h*w,' ') {}
Screen(pos h, pos w, char c):height(h),width(w),content(h*w,c) {}
char get() const
{
return content[curse];
}
inline char get(pos r, pos c) const ;
Screen &move(pos r, pos c);
Screen &set(pos r, pos c, char ch);
Screen &set(char ch);
const Screen &display(ostream& os) const
{
do_display(os);
return *this;
}
Screen &display(ostream &os)
{
do_display(os);
return *this;
}
private:
void do_display(ostream &os) const
{
os << content;
}
pos height,width;
pos curse=0;
string content;
};
inline
Screen &Screen::move(pos r, pos c)
{
pos row = r*width;
curse = row +c;
return *this;
}
inline
Screen &Screen::set(pos r, pos c, char ch)
{
content[r*width+c] = ch;
return *this;
}
inline
Screen &Screen::set(char ch)
{
content[curse] = ch;
return *this;
}
char Screen::get(pos r, pos c) const
{
return content[r*width+c];
}
习题7.33
pos返回类型出现在类名之前,所以事实上他是在类的作用域之外的。
Screen::pos Screen::size() const
{
}
习题7.34
编译器只会考虑在使用别名之前出现的声明,故本题的改动会导致未声明的报错。
习题7.35
类的定义是对的。
如果成员使用了外层作用域中的名字,而该名字代表一种类型,则类不能在之后重新定义该名字。
但是本题的例子中类尚没有成员函数使用过外层作用域的名字,故可以重新定义。
成员函数setVal的定义是错的。因为setType的返回类型本应该是double(内层作用域的别名),但是因为在类外面定义函数时其返回类型Type在外层作用域,是string。
故应改为
Exercise::Type Exercise::setVal(Type parm)
{
}
习题7.36
rem比base更早初始化,所以用base去初始化rem是非法的。
习题7.38
Sales_data(istream &is = cin) { read(is, *this); }
习题7.39
非法。两个默认构造函数会导致非法重载。
习题7.58
有错误
静态数据成员必须在类的外部定义和初始化每个静态成员
然而,我们可以为静态成员提供const整数类型的类内初始值。不过要求静态成员必须是字面值常量类型的constexpr。
所以rate 和 vec的初始化必须在类外进行,而 vecSize 的初始化是正确的