C++ Primer第5版 第七章课后练习答案
合集《C++ Primer第5版》 课后练习答案 - 丸子球球 - 博客园 (cnblogs.com)
练习7.1
struct Sales_data {
string bookNo;
unsigned units_sold = { 0 };
double revenue = { 0.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.revenue += trans.revenue;
total.units_sold += trans.units_sold;
}
else {
cout < total.bookNo < " " < total.units_sold < " " < total.revenue < endl;
total = trans;
}
}
cout < total.bookNo < " " < total.units_sold < " " < total.revenue < endl;
}
else {
std::cerr < "No Data?!" < endl;
return -1;
}
return 0;
}
练习7.2
struct Sales_data {
string bookNo;
unsigned units_sold = { 0 };
double revenue = { 0.0 };
string isbn() { return bookNo; }
Sales_data& combine(const Sales_data& rhs) {
units_sold += rhs.units_sold;
revenue += rhs.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.bookNo < " " < total.units_sold < " " < total.revenue < endl;
total = trans;
}
}
cout < total.bookNo < " " < total.units_sold < " " < total.revenue < endl;
}
else {
std::cerr < "No Data?!" < endl;
return -1;
}
return 0;
}
练习7.4
#ifndef PERSON_H_
#define PERSON_H_
#include <string>
using std::string;
struct Person {
string name;
string address;
};
#endif // !PERSON_H_
练习7.5
struct Person {
string name;
string address;
const string isname() { return name; }
const string isaddress() { return address; }
};
应该是const的,因为我们不需要修改返回值
练习7.6
rhs) {
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
istream &read(istream& is, Sales_data& it) {
is > it.bookNo > it.units_sold > it.revenue;
return is;
}
ostream& print(ostream& os, Sales_data& it) {
os < it.isbn() < " " < it.units_sold < " " < it.revenue < endl;
return os;
}
练习7.7
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 = add(total, trans);
}
else {
print(cout, total);
total = trans;
}
}
print(cout, total);
}
else {
std::cerr < "No Data?!" < endl;
return -1;
}
return 0;
}
练习7.8
print应该是常量引用的,因为我们不需要修改传入值
练习7.9
struct Person {
string name;
string address;
const string isname() { return name; }
const string isaddress() { return address; }
};
istream& read(istream& is, Person& it) {
is > it.name > it.address;
return is;
}
ostream& print(ostream& os, Person& it) {
os < it.isname() < " " < it.isaddress() < endl;
return os;
}
练习7.10
判断读操作对象是否符合要求
练习7.11
struct Sales_data {
string bookNo;
unsigned units_sold = { 0 };
double revenue = { 0.0 };
string isbn() { return bookNo; }
Sales_data& combine(const Sales_data& rhs) {
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
Sales_data(string bn, unsigned us, double re) :bookNo(bn), units_sold(us), revenue(re) {}
Sales_data() = default;
};
int main(int argc, char* argv[])
{
Sales_data total("",0,0);
print(cout, total);
return 0;
}
练习7.12
struct Sales_data {
string bookNo;
unsigned units_sold = { 0 };
double revenue = { 0.0 };
string isbn() { return bookNo; }
Sales_data& combine(const Sales_data& rhs) {
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
Sales_data(string bn, unsigned us, double re) :bookNo(bn), units_sold(us), revenue(re) {}
Sales_data() = default;
Sales_data(istream& is) { read(is, *this); }
};
练习7.13
int main(int argc, char* argv[])
{
Sales_data total(cin);
if (cin) {
Sales_data trans(cin);
do{
if (total.bookNo == trans.bookNo) {
total.revenue += trans.revenue;
total.units_sold += trans.units_sold;
}
else {
cout < total.bookNo < " " < total.units_sold < " " < total.revenue < endl;
total = trans;
}
} while (read(cin, trans));
cout < total.bookNo < " " < total.units_sold < " " < total.revenue < endl;
}
else {
std::cerr < "No Data?!" < endl;
return -1;
}
return 0;
}
练习7.14
string bn, unsigned us, double re) :bookNo(bn), units_sold(us = 0), revenue(re = 0.0) {};
练习7.15
struct Person {
string name;
string address;
const string isname() { return name; }
const string isaddress() { return address; }
Person() = default;
Person(string name, string address) :name(name), address(address) {}
};
练习7.16
没有限定。在整个程序内可被访问到的成员应定义在public说明符之后;可以被类的成员访问但不能被使用该类代码访问的应该定义在private说明符之后。
练习7.17
有区别,struct的默认访问权限是public,class的默认访问权限是class
练习7.18
封装:使用函数指针把属性与方法封装到结构体中
练习7.19
成员函数声明成public,成员变量声明成private
练习7.20
类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。
优点:能访问私有成员
缺点:破坏封装性
练习7.21
class Sales_data {
friend istream& read(istream& is, Sales_data& it);
friend ostream& print(ostream& os, Sales_data& it);
private:
string bookNo;
unsigned units_sold = { 0 };
double revenue = { 0.0 };
public:
const string& isbn() { return bookNo; }
Sales_data& combine(const Sales_data& rhs) {
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
Sales_data(string bn, unsigned us, double re) :bookNo(bn), units_sold(us = 0), revenue(re = 0.0) {};
Sales_data() = default;
Sales_data(istream& is) { read(is, *this); }
};
Sales_data add(Sales_data& lhs, Sales_data& rhs) {
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
istream &read(istream& is, Sales_data& it) {
is > it.bookNo > it.units_sold > it.revenue;
return is;
}
ostream& print(ostream& os, Sales_data& it) {
os < it.isbn() < " " < it.units_sold < " " < it.revenue < endl;
return os;
}
练习7.22
class Person {
friend istream& read(istream& is, Person& it);
friend ostream& print(ostream& os, Person& it);
private:
string name;
string address;
public:
const string isname() { return name; }
const string isaddress() { return address; }
Person() = default;
Person(string name, string address) :name(name), address(address) {}
};
istream& read(istream& is, Person& it) {
is > it.name > it.address;
return is;
}
ostream& print(ostream& os, Person& it) {
os < it.isname() < " " < it.isaddress() < endl;
return os;
}
练习7.23
#ifndef SCREEN_H_
#define SCREEN_H_
#include<string>
class Screen {
public:
using pos = std::string::size_type;
private:
pos cursor = 0;
pos height = 0, width = 0;
std::string contents;
};
#endif // !SCREEN_H_
练习7.24
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) {}
private:
pos cursor = 0;
pos height = 0, width = 0;
std::string contents;
};
练习7.25
可以,因为只有内置类型和可变类型如vector类型,string类型可以依赖于操作的默认版本
练习7.26
在函数体外定义或者类内声明的返回值前添加inline关键字即可
练习7.27
#ifndef SCREEN_H_
#define SCREEN_H_
#include<string>
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) {}
Screen& set(char);
Screen& set(pos, pos,char);
Screen& move(pos, pos);
Screen& display(std::ostream& os);
const Screen& display(std::ostream& os)const;
private:
pos cursor = 0;
pos height = 0, width = 0;
std::string contents;
void do_display(std::ostream& os)const {
os < contents;
}
};
inline Screen& Screen::set(char c) {
contents[cursor] = c;
return *this;
}
inline Screen& Screen::set(pos row, pos col,char c) {
cursor = row * width + col;
contents[row * width + col] = c;
return *this;
}
inline Screen& Screen::move(pos row, pos col) {
char newc = contents[cursor];
contents[cursor] = contents[row * width + col];
contents[row * width + col] = newc;
return *this;
}
inline Screen& Screen::display(std::ostream& os)
{
do_display(os);
return *this;
}
inline const Screen& Screen::display(std::ostream& os) const
{
do_display(os);
return *this;
}
#endif // !SCREEN_H_
练习7.28
若函数返回类型变为Screen,则返回的是对象的副本,函数的操作只能添加于对象的副本上,对象的本身并没有改变。所以两次显示结果会不一样,myScreen不会输出#
练习7.29
xxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxx
推测正确
练习7.30
1:当需要将一个对象作为整体引用而不是引用对象的一个成员时,使用this,则该函数返回对调用该函数的对象的引用。
2:可以非常明确地指出访问的是调用该函数的对象的成员,且可以在成员函数中使用与数据成员同名的形参。
缺点:不必要使用,代码多余。
练习7.31
class X {
Y* y;
};
class Y {
X* x;
};
练习7.32
class Screeen;
class Window_mgr
{
public:
using ScreenIndex=std::vector<Screen>::size_type ;
void clear(ScreenIndex);
private:
std::vector<Screen> screens;
};
"Window_mgr.h"
class Screen {
public:
friend void Window_mgr::clear(ScreenIndex);
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) {}
Screen set(char);
Screen set(pos, pos,char);
Screen move(pos, pos);
Screen display(std::ostream& os);
const Screen display(std::ostream& os)const;
private:
pos cursor = 0;
pos height = 0, width = 0;
std::string contents;
void do_display(std::ostream& os)const {
os < contents;
}
};
inline Screen Screen::set(char c) {
contents[cursor] = c;
return *this;
}
inline Screen Screen::set(pos row, pos col,char c) {
cursor = row * width + col;
contents[row * width + col] = c;
return *this;
}
inline Screen Screen::move(pos row, pos col) {
char newc = contents[cursor];
contents[cursor] = contents[row * width + col];
contents[row * width + col] = newc;
return *this;
}
inline Screen Screen::display(std::ostream& os)
{
do_display(os);
return *this;
}
inline const Screen Screen::display(std::ostream& os) const
{
do_display(os);
return *this;
}
inline void Window_mgr::clear(ScreenIndex i)
{
Screen& s = screens[i];
s.contents = std::string(s.height * s.width, ' ');
}
练习7.33
C++ 成员声明中不允许限定名
const {
return height * width;
}
练习7.34
Screen类的成员声明中出现pos且在pos的typedef之前的的一律报错。
练习7.35
string Type;
Type initval();//外层作用域Money
class Exercise {
public:
typedef double Type;//错误;不能重新定义Money
Type setVal(Type);//Type类内匹配
Type initVal();//Type类内匹配,在类内重新声明initVal函数
private:
int val;
};
Type Exercise::setVal(Type parm) {//外层作用域Type
val = parm + initVal();
return val;
}
修改后
string Type;
Type initval();
class Exercise {
public:
typedef double Double_type;//修改类内的类型名与外部作用域类型名不相同
Double_type setVal(Double_type);
Double_type initVal();
private:
int val;
};
Exercise::Double_type Exercise::setVal(Double_type parm) {
val = parm + initVal();
return val;
}
练习7.36
struct X
{
X(int i, int j) :base(i), rem(base% j) {}//试图使用未初始化的base来初始化rem
int rem, base;
};
更改成员出现顺序来更改成员的初始化顺序
struct X
{
X(int i, int j) :base(i), rem(base% j) {}
int base, rem;
};
练习7.37
//Sales_data(std::istream &is){read(is,*this) };与传入的值相关
int main(int argc, char* argv[])
{
Sales_data next;//Sales_data(string s="") :bookNo(s){};cnt = 0, revenue = 0.0
Sales_data last("9-999-99999-9");//Sales_data(string bn, unsigned cnt, double rev) :bookNo(bn), units_sold(cnt), revenue(cnt*rev) {};bookNo = "9-999-99999-9", cnt = 0, revenue = 0.0
}
练习7.38
is = std::cin) { read(is, *this); }
练习7.39
不合法,这样的话接受string的构造函数和接受istream&的构造函数都可以接受无参函数,编译器无法判断要调用哪个函数
练习7.40
#ifndef TREE_H_
#define TREE_H_
#include <vector>
#include <string>
/*每棵树上都有很多分叉,每个分叉上都可能长着很多片叶子,
所以用Tree来描述树以及树上的枝干,用string来代表每一个枝干上的叶子
*/
class Tree
{
public:
using leaf = std::string;
Tree(std::vector<Tree> t, std::vector<leaf> l) : trees{ t }, leaves{ l } { }
Tree(std::vector<Tree> t, leaf l) : trees{ t }, leaves{ l } { }
Tree(Tree t, std::vector<leaf> l) : trees{ t }, leaves{ l } { }
Tree(Tree t, leaf l) : trees{ t }, leaves{ l } { }
void setTree(Tree t);
void setTrees(std::vector<Tree> t);
void setLeaf(leaf l);
void setLeaves(std::vector<leaf> l);
std::vector<Tree> getTrees();
std::vector<leaf> getLeaves();
private:
std::vector<Tree> trees;
std::vector<leaf> leaves;
};
#endif // !TREE_H_
练习7.41
string bn, unsigned us = 0, double re = 0.0) :bookNo(bn), units_sold(us), revenue(re) { std::cout < "Sales_data(string bn, unsigned us = 0, double re = 0.0)" < std::endl;}
Sales_data() :Sales_data("") { std::cout < "Sales_data()" < std::endl; }
Sales_data(istream& is) :Sales_data() { read(is, *this); std::cout < "Sales_data(istream& is)" < std::endl; }
Sales_data first_item(cin);
Sales_data next;
Sales_data last("9-999-99999-9");
string bn, unsigned us = 0, double re = 0.0)
Sales_data()
1 1 1
Sales_data(istream& is)
Sales_data(string bn, unsigned us = 0, double re = 0.0)
Sales_data()
Sales_data(string bn, unsigned us = 0, double re = 0.0)
练习7.42
class Tree
{
public:
using leaf = std::string;
Tree(std::vector<Tree> t, std::vector<leaf> l) : trees(t), leaves(l) { }
Tree(std::vector<Tree> t, leaf l) : Tree(t, std::vector<leaf>{l}) { }
Tree(Tree t, std::vector<leaf> l) : Tree(std::vector<Tree>{t}, l) { }
Tree(Tree t, leaf l) : Tree(std::vector<Tree>{t}, std::vector<leaf>{l}) { }
void setTree(Tree t);
void setTrees(std::vector<Tree> t);
void setLeaf(leaf l);
void setLeaves(std::vector<leaf> l);
std::vector<Tree> getTrees();
std::vector<leaf> getLeaves();
private:
std::vector<Tree> trees;
std::vector<leaf> leaves;
};
练习7.43
class NoDefault
{
public:
NoDefault(int x) {};
private:
};
class C
{
public:
C() :noDefault(0) {};
private:
NoDefault noDefault;
};
练习7.44
不合法,因为NoDefault类没有默认构造函数,所以不能无参初始化
练习7.45
合法,因为C类有默认构造函数,而且能对成员类初始化
练习7.46
(a)不正确,如果类不提供构造函数,编译器会自动创建一个合成的默认构造函数
(b)不正确,当对象被默认初始化或值初始化都自动执行默认构造函数
(c)不正确,值初始化也需要类提供默认构造函数
(d)不正确,类没有定义任何构造函数的时候,编译器就会自动生成一个默认构造函数
练习7.47
应该。优点是可以避免隐式的类类型转换带来的风险,生成一个隐式转换后的类临时变量,完成操作后就消失了。缺点是用户使用时需要显式的创建临时对象,对用户提高了要求
练习7.48
string null_isbn("9-99-9999-9");
Sales_data item1(null_isbn); // 用string类型的null_isbn直接初始化item1
Sales_data item2("9-99-9999-9"); // 用字符串字面值初始化item2.
练习7.49
// 正常初始化,将s转化成Sales_data类型。
Sales_data &combine(Sales_data&);// 报错,string不能转化为Sales_data类型的引用。
Sales_data &combine(const Sales_data&) const;//报错,常量函数不允许对值做出改变。
练习7.50
explicit Person(istream& in) { read(in, *this); }//只有该构造函数是传入一个参数的
练习7.51
string存在const char*的隐式转换,而vector如果允许隐式转换则会提高理解难度
练习7.52
聚合类的原理(编译器遇到Plain OI' Data声明形式由于与C程序兼容,所以可以使用C程序形式进行初始化,从C++20开始, POD这一概念就被废止, 取而代之的是一系列更为精确的定义, 如TrivialType)
要使用下面这种方式初始化则必然是聚合类,但是聚合类要求没有类内初始值,所以应该修改为
struct Sales_data
{
std::string bookNo;
unsigned units_sold;
double revenue;
};
练习7.53
class Debug
{
public:
constexpr Debug(bool h, bool i, bool o) :hw(h), io(i), other(o) {};
constexpr Debug(bool b = true) :Debug(b, b, b) {};
constexpr bool any() { return hw || io || other; }
void set_hw(bool b) { hw = b; }
void set_io(bool b) { io = b; }
void set_other(bool b) { other = b; }
private:
bool hw;
bool io;
bool other;
};
练习7.54
constexpr函数的返回值和所有形参的类型都必须得是字面值类型,而且函数体中必须有且只有一条return语句,可包含其他语句但不能在运行期起作用,例如;空语句等
所以可以被声明为constexpr
练习7.55
聚合类是字面值常量类,因为聚合类初始化必须使用字面值列表来进行初始化
练习7.56
和类本身相关但不是和类的对象保持联系的成员是类的静态成员,在成员声明前加上static关键字
优点:和类关联但不和类的对象关联,一旦修改所有对象都能使用新值
静态成员和普通成员的区别:不是在创建类的对象时被定义,而是在类的外部定义和初始化,因此静态数据成员可以是不完全类型。类的静态成员属于类本身,在类加载时就会分配内存,可以通过类名直接进行访问。普通成员属于类的对象,只有在类对象产生时才会分配内存。只能通过对象去访问。
练习7.57
class Account
{
public:
void calculate() { amount += amount * interestRate; }
static double rate() { return interestRate; }
static void rate(double);
private:
string owner;
double amount;
static double interestRate;
static double initRate();
};
练习7.58
static double rate = 6.5;//成员本身不是常量表达式
static const int vecSize = 20;
static std::vector<double> vec(vecSize);//vector对象不能在类内初始化