C++Primer第五版 习题答案 【总目录】:https://blog.csdn.net/Dust_Evc/article/details/114334124
7.1
#include <iostream>
#include <string>
using std::cin; using std::cout; using std::endl; using std::string;
struct Sales_data
{
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
int main()
{
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 = trans;
}
}
cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;
}
else
{
std::cerr << "No data?!" << std::endl;
return -1;
}
return 0;
}
7.2
文件名命名为:Sales_data_7_2.h,后面习题需要使用该头文件。
#pragma once
#ifndef SALES_DATA_7_2_H
#define SALES_DATA_7_2_H
#include <string>
#include <iostream>
using namespace std;
struct Sales_data
{
// 数据成员和2.6.1节 (第64页)相比没有改变
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
// 新成员:关于Sales_data对象的操作
string isbn()
const { return bookNo; }
Sales_data& combine(const Sales_data&);
};
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold; //等同于this.units_sold += rhs.units_sold
revenue += rhs.revenue; //等同于this.revenue += rhs.revenue
return *this; //返回调用该函数的对象,即this所绑定的对象
}
#endif // !SALES_DATA_7_2_H
7.3
#include "Sales_data_7_2.h"
int main()
{
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); //total的地址被绑定到combine定义中隐式的this参数上,返回类型是Sales_data&
else
{
cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;
total = trans;
}
}
cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;
}
else
{
cerr << "No data?!" << endl;
return -1;
}
return 0;
}
7.4
#pragma once
#ifndef PERSON_7_4_H
#define PERSON_7_4_H
#include <string>
using namespace std;
struct Person
{
string Name, Address;
};
#endif // !PERSON_7_4_H
7.5
#pragma once
#ifndef PERSON_7_5_H
#define PERSON_7_5_H
#include <string>
using namespace std;
struct Person
{
//定义数据成员
string Name, Address;
//定义成员函数(方法)
string& const Get_Name() //只可访问,不可修改
{ return Name; }
string& const Get_Address() //只可访问,不可修改
{ return Address; }
};
#endif // !PERSON_7_5_H
应该是const的。因为常量的Person对象也需要使用这些函数操作。
7.6
文件名命名为:Sales_data_7_6.h,后面习题需要使用该头文件。
#pragma once
#ifndef SALES_DATA_7_6_H
#define SALES_DATA_7_6_H
#include <string>
#include <iostream>
using namespace std;
struct Sales_data
{
// 数据成员
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
// 新成员:关于Sales_data对象的操作
string isbn()
const { return bookNo; }
Sales_data& combine(const Sales_data&);
};
//类外函数定义
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold; //等同于this.units_sold += rhs.units_sold
revenue += rhs.revenue; //等同于this.revenue += rhs.revenue
return *this; //返回调用该函数的对象,即this所绑定的对象
}
istream &read(istream &is, Sales_data &item) //因为IO类属于不能被拷贝的类型,因此只能通过引用来传递它们
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = item.units_sold * price;
return is;
}
ostream &print(ostream &os, const Sales_data &item) //因为IO类属于不能被拷贝的类型,因此只能通过引用来传递它们
{
os << item.isbn() << " " << item.units_sold << " " << item.revenue;
return os;
}
Sales_data add(const Sales_data &item1, const Sales_data &item2)
{
Sales_data sum = item1;
sum.combine(item2);
return sum;
}
#endif // !SALES_DATA_7_6_H
7.7
#include "Sales_data_7_6.h"
int main()
{
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) << endl;
total = trans;
}
}
print(cout, total) << endl;
}
else
{
cerr << "No data?!" << endl;
return -1;
}
return 0;
}
7.8
因为read函数中需要改变对象的内容,而print函数中不需改变对象的内容。
7.9
#pragma once
#ifndef PERSON_7_9_H
#define PERSON_7_9_H
#include <string>
#include <iostream>
using namespace std;
struct Person
{
//定义数据成员
string Name, Address;
//定义成员函数(方法)
string& const Get_Name() //只可访问,不可修改
{ return Name; }
string& const Get_Address() //只可访问,不可修改
{ return Address; }
};
istream &read(istream &is, Person &item)
{
is >> item.Name >> item.Address;
return is;
}
ostream &print(ostream &os, const Person &item)
{
os << item.Name << " " << item.Address << " " << endl;
return os;
}
#endif // !PERSON_7_9_H
7.10
read 函数的返回值是 istream 对象,if语句中条件部分的作用是从输入流中读取数据给两个data对象。
7.11
Sales_data_7_11.h
#pragma once
#ifndef SALES_DATA_7_11_H
#define SALES_DATA_7_11_H
#include <string>
#include <iostream>
using namespace std;
struct Sales_data
{
// 数据成员(2.6节)
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
// 新成员:关于Sales_data对象的操作(7.1.2节)
string isbn() const
{
return bookNo;
}
Sales_data& combine(const Sales_data&);
//新增类内构造函数(7.1.4节),构造函数名字与类名相同
Sales_data() = default; //此种情况,指令编译器执行合成的默认构造函数来初始化
Sales_data(istream &is);
Sales_data(const string &s) : bookNo(s) { }; 构造函数中未给定初始值的部分,利用数据成员初始值进行初始化
Sales_data(const string &s, unsigned n, double p) : bookNo(s), units_sold(n), revenue(n*p) { };
};
//类外函数定义(7.1.3节)
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold; //等同于this.units_sold += rhs.units_sold
revenue += rhs.revenue; //等同于this.revenue += rhs.revenue
return *this; //返回调用该函数的对象,即this所绑定的对象
}
istream &read(istream &is, Sales_data &item) //因为IO类属于不能被拷贝的类型,因此只能通过引用来传递它们
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = item.units_sold * price;
return is;
}
ostream &print(ostream &os, const Sales_data &item) //因为IO类属于不能被拷贝的类型,因此只能通过引用来传递它们
{
os << item.isbn() << " " << item.units_sold << " " << item.revenue;
return os;
}
Sales_data add(const Sales_data &item1, const Sales_data &item2)
{
Sales_data sum = item1;
sum.combine(item2);
return sum;
}
//新增类外构造函数(7.1.4节)
Sales_data::Sales_data(istream &is) //此构造函数初始值列表是空的,通过函数体进行初始化
{
read(is, *this); //从is中读取一条交易信息然后存入this所绑定的对象中
}
#endif // !SALES_DATA_7_11_H
cpp文件
#include "Sales_data_7_11.h"
int main() // 逐步执行来明白构造函数的运行原理
{
Sales_data item1; //构造函数中未给定初始值的部分,利用数据成员初始值进行初始化
print(cout, item1) << endl;
Sales_data item2("0-201-88954-4"); //白字item2对应的就是头文件构造函数定义中的白字Sales_data
print(cout, item2) << endl;
Sales_data item3("0-201-88954-4", 5, 20.00); //构造函数中给定初始值的情况,无需利用数据成员初始值进行初始化
print(cout, item3) << endl;
Sales_data item4(cin); //使用构造函数初始化时,整体语法与类外构造函数的定义形式相似
print(cout, item4) << endl;
return 0;
}
7.12
#pragma once
#ifndef SALES_DATA_7_12_H
#define SALES_DATA_7_12_H
#include <string>
#include <iostream>
using namespace std;
//为将类外构造函数移到类内,需要先加下面两行声明read,否则第31行报错:'read' identifier not found
struct Sales_data;
istream &read(istream &is, Sales_data &item);
struct Sales_data
{
// 数据成员(2.6节)
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
// 新成员:关于Sales_data对象的操作(7.1.2节)
string isbn() const
{
return bookNo;
}
Sales_data& combine(const Sales_data&);
//新增类内构造函数(7.1.4节),构造函数名字与类名相同
Sales_data() = default; //此种情况,指令编译器执行合成的默认构造函数来初始化
Sales_data(istream &is) { read(is, *this); }; //将7.11.h中的类外构造函数移到类内
Sales_data(const string &s) : bookNo(s) { }; 构造函数中未给定初始值的部分,利用数据成员初始值进行初始化
Sales_data(const string &s, unsigned n, double p) : bookNo(s), units_sold(n), revenue(n*p) { };
};
//类外函数定义(7.1.3节)
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold; //等同于this.units_sold += rhs.units_sold
revenue += rhs.revenue; //等同于this.revenue += rhs.revenue
return *this; //返回调用该函数的对象,即this所绑定的对象
}
istream &read(istream &is, Sales_data &item) //因为IO类属于不能被拷贝的类型,因此只能通过引用来传递它们
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = item.units_sold * price;
return is;
}
ostream &print(ostream &os, const Sales_data &item) //因为IO类属于不能被拷贝的类型,因此只能通过引用来传递它们
{
os << item.isbn() << " " << item.units_sold << " " << item.revenue;
return os;
}
Sales_data add(const Sales_data &item1, const Sales_data &item2)
{
Sales_data sum = item1;
sum.combine(item2);
return sum;
}
#endif // !SALES_DATA_7_12_H
7.13
#include "Sales_data_7_12.h"
int main()
{
Sales_data total(cin); //【★★】
if (!total.isbn().empty()) //【★★】
{
istream &is = cin; //【★★★★】
while (is) //【★★】
{
Sales_data trans(is); //【★★】
if (total.isbn() == trans.bookNo)
{
total.combine(trans);
}
else
{
print(cout, total);
total = trans;
}
}
print(cout, total);
}
else
{
cerr << "No data?!" << endl;
return -1;
}
return 0;
}
7.14
编写的类内构造函数如下:
Sales_data() : units_sold(0) , revenue(0) { }
7.15
#pragma once
#ifndef PERSON_7_15_H
#define PERSON_7_15_H
#include <string>
#include <iostream>
using namespace std;
struct Person;
istream &read(istream &is, Person &item);
struct Person
{
//定义数据成员 (Ex_7.4)
string Name, Address;
//定义成员函数(方法) (Ex_7.5)
string& const Get_Name() //只可访问,不可修改
{
return Name;
}
string& const Get_Address() //只可访问,不可修改
{
return Address;
}
//定义构造函数(Ex_7.15)
Person() = default;
Person(istream &is) { read(is, *this); };
Person(string &NM) : Name(NM) { };
Person(string &NM, string &ADD) : Name(NM), Address(ADD) { };
};
//定义类外函数 (Ex_7.9)
istream &read(istream &is, Person &item)
{
is >> item.Name >> item.Address;
return is;
}
ostream &print(ostream &os, const Person &item)
{
os << item.Name << " " << item.Address << " " << endl;
return os;
}
#endif // !PERSON_7_15_H
7.16
在类的定义中对于访问说明符出现的位置和次数没有限定。每个访问说明符指定了接下来的成员的访问级别,其有效范围直到出现下一个访问说明符或者达到类的结尾处为止。
如果某个成员需要在整个程序内都被访问,那么它应该定义为 public; 如果某个成员只能在类内部访问,那么它应该定义为 private。
7.17
class 和 struct 的唯一区别是默认的访问级别不同。
struct关键字,定义在第一个访问说明符之前的成员是public 的;相反,class关键字,定义在第一个访问说明符之前的成员是private的。
7.18
将类内部分成员设置为外部不可见,而提供部分接口给外面,这样的行为叫做封装。
封装隐藏了具体的实现,当我们使用某个抽象数据类型时,只需要考虑它提供什么接口操作,而不用去考虑它的具体实现。
7.19
构造函数、getName()、getAddress() 函数将设为 public。name 和 address 将设为private。
函数是暴露给外部的接口,因此要设为public;而数据则应该隐藏让外部不可见。
7.20
利: 与当前类有关的接口函数能直接访问类的私有变量。
弊: 牺牲了封装性与可维护性。
7.21
#pragma once
#ifndef SALES_DATA_7_21_H
#define SALES_DATA_7_21_H
#include <string>
#include <iostream>
using namespace std;
//为将类外构造函数移到类内,需要先加下面两行声明read,否则第31行报错:'read' identifier not found
struct Sales_data;
istream &read(istream &is, Sales_data &item);
struct Sales_data
{
//友元声明(Ex_7.21)
friend istream &read(istream &is, Sales_data &item);
friend ostream &print(ostream &os, const Sales_data &item);
friend Sales_data add(const Sales_data &item1, const Sales_data &item2);
private:
// 数据成员(2.6节)
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
public:
// 新成员:关于Sales_data对象的操作(7.1.2节)
string isbn() const
{
return bookNo;
}
Sales_data& combine(const Sales_data&);
//新增类内构造函数(7.1.4节),构造函数名字与类名相同
Sales_data() = default; //此种情况,指令编译器执行合成的默认构造函数来初始化
Sales_data(istream &is) { read(is, *this); }; //将7.11.h中的类外构造函数移到类内
Sales_data(const string &s) : bookNo(s) { }; 构造函数中未给定初始值的部分,利用数据成员初始值进行初始化
Sales_data(const string &s, unsigned n, double p) : bookNo(s), units_sold(n), revenue(n*p) { };
};
//类外函数定义(7.1.3节)
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold; //等同于this.units_sold += rhs.units_sold
revenue += rhs.revenue; //等同于this.revenue += rhs.revenue
return *this; //返回调用该函数的对象,即this所绑定的对象
}
istream &read(istream &is, Sales_data &item) //因为IO类属于不能被拷贝的类型,因此只能通过引用来传递它们
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = item.units_sold * price;
return is;
}
ostream &print(ostream &os, const Sales_data &item) //因为IO类属于不能被拷贝的类型,因此只能通过引用来传递它们
{
os << item.isbn() << " " << item.units_sold << " " << item.revenue;
return os;
}
Sales_data add(const Sales_data &item1, const Sales_data &item2)
{
Sales_data sum = item1;
sum.combine(item2);
return sum;
}
#endif // !SALES_DATA_7_21_H
7.22
#pragma once
#ifndef PERSON_7_22_H
#define PERSON_7_22_H
#include <string>
#include <iostream>
using namespace std;
struct Person;
istream &read(istream &is, Person &item);
struct Person
{
//友元声明(Ex_7.21)
friend istream &read(istream &is, Person &item);
friend ostream &print(ostream &os, const Person &item);
private:
//定义数据成员 (Ex_7.4)
string Name, Address;
public:
//定义成员函数(方法) (Ex_7.5)
string& const Get_Name() //只可访问,不可修改
{
return Name;
}
string& const Get_Address() //只可访问,不可修改
{
return Address;
}
//定义构造函数(Ex_7.15)
Person() = default;
Person(istream &is) { read(is, *this); };
Person(string &NM) : Name(NM) { };
Person(string &NM, string &ADD) : Name(NM), Address(ADD) { };
};
//定义类外函数 (Ex_7.9)
istream &read(istream &is, Person &item)
{
is >> item.Name >> item.Address;
return is;
}
ostream &print(ostream &os, const Person &item)
{
os << item.Name << " " << item.Address << " " << endl;
return os;
}
#endif // !PERSON_7_22_H
7.23
#ifndef CP5_ex7_23_h
#define CP5_ex7_23_h
#include <string>
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
7.24
#ifndef CP5_ex7_24_h
#define CP5_ex7_24_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) {}
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
7.25
能。
Screen 的成员只有内置类型和 string,因此能安全地依赖于拷贝和赋值操作的默认版本。管理动态内存的类则不能依赖于拷贝和赋值操作的默认版本,而且也应该尽量使用string 和 vector 来避免动态管理内存的复杂性。
7.26
#ifndef CP5_ex7_26_h
#define CP5_ex7_26_h
#include <string>
#include <iostream>
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
7.27
exercise7_27.h
#pragma once
#ifndef CP5_ex7_27_h
#define CP5_ex7_27_h
#include <string>
#include <iostream>
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
cpp
#include "exercise7_27.h"
int main()
{
Screen myScreen(5, 5, 'X');
myScreen.move(4, 0).set('#').display(std::cout);
std::cout << "\n";
myScreen.display(std::cout);
std::cout << "\n";
return 0;
}
7.28
如果返回类型是Screen,那么move返回的是 *this 的一个副本,因此set函数只能改变临时副本而不能改变myScreen的值。
7.29
推测正确。
7.30
优点:
1:程序的意图更明确。当需要将一个对象作为整体引用而不是引用对象的一个成员时,使用this,则该函数返回对调用该函数的对象的引用。
2:可以非常明确地指出访问的是调用该函数的对象的成员,且可以在成员函数中使用与数据成员同名的形参。
缺点:有时候显得有点多余。
7.31
class Y;
class X{
Y* y = nullptr;
};
class Y{
X x;
};
7.32
#ifndef CP5_ex7_32_h
#define CP5_ex7_32_h
#include <vector>
#include <iostream>
#include <string>
class Screen;
class Window_mgr
{
public:
using ScreenIndex = std::vector<Screen>::size_type;
inline void clear(ScreenIndex);
private:
std::vector<Screen> screens;
};
class Screen
{
friend void Window_mgr::clear(ScreenIndex);
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]; }
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 width = 0, height = 0;
std::string contents;
};
inline void Window_mgr::clear(ScreenIndex i)
{
Screen& s = screens[i];
s.contents = std::string(s.height*s.width,' ');
}
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
7.33
未定义标识符 pos。应该改为:
Screen::pos Screen::size() const
{
return height * width;
}
7.34
在 dummy_fcn(pos height)函数中会出现“未定义的标识符pos”。
类型名的定义通常出现在类的开始处,这样就能确保所有使用该类型的成员都出现在类名的定义之后。
7.35
在类中,如果成员使用了外层作用域中的某个名字,而该名字代表一种类型,则类不能在之后重新定义该名字。
因此重复定义 Type 是错误的行为。
7.36
应该改为:
struct X {
X (int i, int j): base(i), rem(base % j) {}
int base, rem;
};
7.37
Sales_data first_item(cin); // 使用 Sales_data(std::istream &is) ; 各成员值从输入流中读取
int main() {
Sales_data next; // 使用默认构造函数 bookNo = "", cnt = 0, revenue = 0.0
// 使用 Sales_data(std::string s = ""); bookNo = "9-999-99999-9", cnt = 0, revenue = 0.0
Sales_data last("9-999-99999-9");
}
7.38
Sales_data(std::istream &is = std::cin) { read(is, *this); }
7.39
不合法。当你调用 Sales_data()
构造函数时,无法区分是哪个重载。
7.40
(a) Book.
class Book
{
public:
Book(unsigned isbn, std::string const& name, std::string const& author, std::string const& pubdate)
:isbn_(isbn), name_(name), author_(author), pubdate_(pubdate)
{ }
explicit Book(std::istream &in)
{
in >> isbn_ >> name_ >> author_ >> pubdate_;
}
private:
unsigned isbn_;
std::string name_;
std::string author_;
std::string pubdate_;
};
7.41
头文件
#ifndef CP5_ex7_41_h
#define CP5_ex7_41_h
#include <string>
#include <iostream>
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(const std::string &s, unsigned n, double p) :bookNo(s), units_sold(n), revenue(n*p)
{
std::cout << "Sales_data(const std::string&, unsigned, double)" << std::endl;
}
Sales_data() : Sales_data("", 0, 0.0f)
{
std::cout << "Sales_data()" << std::endl;
}
Sales_data(const std::string &s) : Sales_data(s, 0, 0.0f)
{
std::cout << "Sales_data(const std::string&)" << std::endl;
}
Sales_data(std::istream &is);
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
cpp文件
#include "exercise7_41.h"
Sales_data::Sales_data(std::istream &is) : Sales_data()
{
std::cout << "Sales_data(istream &is)" << std::endl;
read(is, *this);
}
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;
}
main文件
#include "exercise7_41.h"
using std::cout; using std::endl;
int main()
{
cout << "1. default way: " << endl;
cout << "----------------" << endl;
Sales_data s1;
cout << "\n2. use std::string as parameter: " << endl;
cout << "----------------" << endl;
Sales_data s2("CPP-Primer-5th");
cout << "\n3. complete parameters: " << endl;
cout << "----------------" << endl;
Sales_data s3("CPP-Primer-5th", 3, 25.8);
cout << "\n4. use istream as parameter: " << endl;
cout << "----------------" << endl;
Sales_data s4(std::cin);
return 0;
}
7.42
class Book
{
public:
Book(unsigned isbn, std::string const& name, std::string const& author, std::string const& pubdate)
:isbn_(isbn), name_(name), author_(author), pubdate_(pubdate)
{ }
Book(unsigned isbn) : Book(isbn, "", "", "") {}
explicit Book(std::istream &in)
{
in >> isbn_ >> name_ >> author_ >> pubdate_;
}
private:
unsigned isbn_;
std::string name_;
std::string author_;
std::string pubdate_;
};
7.43
class NoDefault {
public:
NoDefault(int i) { }
};
class C {
public:
C() : def(0) { }
private:
NoDefault def;
};
7.44
不合法。因为 NoDefault 没有默认构造函数。
7.45
合法。因为 C
有默认构造函数。
7.46
- (a) 不正确。如果我们的类没有显式地定义构造函数,那么编译器就会为我们隐式地定义一个默认构造函数,并称之为合成的默认构造函数。
- (b) 不完全正确。为每个参数都提供了默认值的构造函数也是默认构造函数。
- (c) 不正确。哪怕没有意义的值也需要初始化。
- (d) 不正确。只有当一个类没有定义任何构造函数的时候,编译器才会生成一个默认构造函数。
7.47
是否需要从 string 到 Sales_data 的转换依赖于我们对用户使用该转换的看法。在此例中,这种转换可能是对的。null_book 中的 string 可能表示了一个不存在的 ISBN 编号。
优点:
- 可以抑制构造函数定义的隐式转换
缺点:
- 为了转换要显式地使用构造函数
7.48
这些定义和是不是 explicit 的无关。
7.49
(a) Sales_data &combine(Sales_data); // ok
(b) Sales_data &combine(Sales_data&); // error C2664: 无法将参数 1 从“std::string”转换为“Sales_data &”
(c) Sales_data &combine(const Sales_data&) const; // 该成员函数是const 的,意味着不能改变对象。而 combine函数的本意就是要改变对象
7.50
explicit Person(std::istream &is){ read(is, *this); }
7.51
假如我们有一个这样的函数:
int getSize(const std::vector<int>&);
如果vector没有将单参数构造函数定义成 explicit 的,我们就可以这样调用:
getSize(34);
很明显这样调用会让人困惑,函数实际上会初始化一个拥有34个元素的vector的临时量,然后返回34。但是这样没有任何意义。而 string 则不同,string 的单参数构造函数的参数是 const char * ,因此凡是在需要用到 string 的地方都可以用 const char * 来代替(字面值就是 const char *)。如:
void print(std::string);
print("hello world");
7.52
Sales_data 类不是聚合类,应该修改成如下:
struct Sales_data {
std::string bookNo;
unsigned units_sold;
double revenue;
};
7.53
class Debug {
public:
constexpr Debug(bool b = true) : hw(b), io(b), other(b) { }
constexpr Debug(bool h, bool i, bool o) : hw(r), io(i), other(0) { }
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; // runtime error
bool io; // I/O error
bool other; // the others
};
7.54
不能。constexpr 函数必须包含一个返回语句。
7.55
不是。因为 std::string 不是字面值类型。
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();
7.58
rate 应该是一个常量表达式。而类内只能初始化整型类型的静态常量,所以不能在类内初始化vec。修改后如下:
// example.h
class Example {
public:
static constexpr double rate = 6.5;
static const int vecSize = 20;
static vector<double> vec;
};
// example.C
#include "example.h"
constexpr double Example::rate;
vector<double> Example::vec(Example::vecSize);