为什么中间几章没有了呢,因为那些和C都差不多,大一学过C语言基本都会了
这本书老是惦记它的Sales_data,经常搞得我晕头转向。
7.1
这一题就是把那时的程序的Sales_item类改成Sales_data类重写一下
#include <iostream>
#include <string>
using namespace std;
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
#include <string>
struct Sales_data {
std::string isbn() const { return bookNo; };
Sales_data& combine(const Sales_data&);
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;
}
7.3
修改程序基本也是照抄。
#include<iostream>
#include<string>
using namespace std;
struct Sales_data {
std::string isbn() const { return bookNo; };
Sales_data& combine(const Sales_data&);
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
//Sales_data的非接口成员函数
Sales_data add(const Sales_data&, const Sales_data&);
ostream& print(ostream&, const Sales_data&);
istream& read(istream&, Sales_data&);
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
int main()
{
Sales_data total;
if (read(cin,total))
{
Sales_data trans;
while (cin >> trans.bookNo >> trans.units_sold >> trans.revenue) {
if (total.isbn() == trans.isbn())
total.combine(trans);
else {
print(cout, total);
total = trans;
}
}
print(cout, total);
}
else
{
std::cerr << "No data?!" << std::endl;
return -1;
}
return 0;
}
7.4
这本书的练习里让你编写的类或者函数记得留着,它经常会让你复用,到时候找不到重新写的话老麻烦了。
struct Person {
string name;
string address;
};
7.5
当然应该是了,因为获取函数是不会改变类里面的内容的。
struct Person {
string name;
string address;
string get_name() const { return name; }
string get_address() const { return address; }
};
7.6
#include<iostream>
#include<string>
using namespace std;
struct Sales_data {
std::string isbn() const { return bookNo; };
Sales_data& combine(const Sales_data&);
int avg_price() const { return revenue / units_sold; }
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
//Sales_data的非接口成员函数
Sales_data add(const Sales_data& lhs, const Sales_data& rhs) {
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
ostream& print(ostream& os, const Sales_data& item) {
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.avg_price();
return os;
}
istream& read(istream& is, Sales_data& item) {
double price = 0;
//输入ISBN、售出总数和售出价格
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
7.7
#include<iostream>
#include<string>
using namespace std;
struct Sales_data {
std::string isbn() const { return bookNo; };
Sales_data& combine(const Sales_data&);
int avg_price() const { return revenue / units_sold; }
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
//Sales_data的非接口成员函数
Sales_data add(const Sales_data& lhs, const Sales_data& rhs) {
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
ostream& print(ostream& os, const Sales_data& item) {
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.avg_price();
return os;
}
istream& read(istream& is, Sales_data& item) {
double price = 0;
//输入ISBN、售出总数和售出价格
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
int main()
{
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?!" << std::endl;
return -1;
}
return 0;
}
7.8
因为read需要修改Sales_data,所以是普通的引用,print只是打印数据,并不修改它,所以是常量引用。
7.9
#include<iostream>
#include<string>
using namespace std;
struct Person {
string name;
string address;
string get_name() const { return name; }
string get_address() const { return address; }
};
istream& read(istream& is, Person& p) {
is >> p.name >> p.address;
return is;
}
ostream& print(ostream& os, const Person& p) {
os << p.name << " " << p.address;
return os;
}
7.10
连续读入data1和data2
7.11
#include<iostream>
#include<string>
using namespace std;
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(p*n){}
Sales_data(istream&);
std::string isbn() const { return bookNo; };
Sales_data& combine(const Sales_data&);
int avg_price() const { return revenue / units_sold; }
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
Sales_data::Sales_data(istream& is) {
read(is, *this);//这个read前面有,就不再贴定义了
}
7.12
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(p*n){}
Sales_data(istream& is) {
read(is, *this);
}
std::string isbn() const { return bookNo; };
Sales_data& combine(const Sales_data&);
int avg_price() const { return revenue / units_sold; }
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
7.13
#include<iostream>
#include<string>
using namespace std;
struct Sales_data;
istream& read(istream& is, Sales_data& item);
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(p*n){}
Sales_data(istream& is) {
read(is, *this);
}
std::string isbn() const { return bookNo; };
Sales_data& combine(const Sales_data&);
int avg_price() const { return revenue / units_sold; }
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
//Sales_data的非接口成员函数
Sales_data add(const Sales_data& lhs, const Sales_data& rhs) {
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
ostream& print(ostream& os, const Sales_data& item) {
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.avg_price();
return os;
}
istream& read(istream& is, Sales_data& item) {
double price = 0;
//输入ISBN、售出总数和售出价格
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
int main()
{
Sales_data total(cin);
if (!total.bookNo.empty())
{
while (cin) {
Sales_data trans(cin);
if (!cin) break;
if (total.isbn() == trans.isbn())
total = add(total, trans);
else {
print(cout, total);
total = trans;
}
}
print(cout, total);
}
else
{
std::cerr << "No data?!" << std::endl;
return -1;
}
return 0;
}
7.14
struct Sales_data;
istream& read(istream& is, Sales_data& item);
struct Sales_data {
Sales_data() = default;
//修改了一下这个构造函数
Sales_data(const string &s):bookNo(s),units_sold(0),revenue(0.0){}
Sales_data(const string &s,unsigned n,double p):bookNo(s),units_sold(n),revenue(p*n){}
Sales_data(istream& is) {
read(is, *this);
}
std::string isbn() const { return bookNo; };
Sales_data& combine(const Sales_data&);
int avg_price() const { return revenue / units_sold; }
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
7.15
struct Person {
string name;
string address;
Person() = default;
Person(const string& n, const string& a) :name(n), address(a) {}
string get_name() const { return name; }
string get_address() const { return address; }
};
7.16
次数没有限定,位置应该也没有。
对外接口应该定义在public之后。内部不可见的数据应该定义在private之后。
7.17
有一点。默认访问权限不同。struct的默认访问权限是public,class的默认访问权限是private
7.18
封装的主要目的是隐藏类的实现细节,只暴露对外部代码有意义的接口。
7.19
class Person {
public:
Person() = default;
Person(const string& n, const string& a) :name(n), address(a) {}
string get_name() const { return name; }
string get_address() const { return address; }
private:
string name;
string address;
};
姓名和地址是一个人的私有数据,不对外开放,用户不能修改,只能通过对外接口get来得到。
7.20
友元的使用场景:
访问私有成员: 当一个类需要将其他类或函数作为友元时,这些友元可以访问被声明为私有的该类成员。
提高性能: 在一些情况下,为了提高性能,可能需要允许其他类或函数直接访问私有成员,而不通过公共接口。
友元的利与弊:
利:
访问私有成员: 友元能够访问被声明为私有的类成员,这在某些特殊情况下是有用的,例如提供更高效的实现或解决特定的设计问题。
灵活性: 友元提供了一定的灵活性,允许某些类或函数绕过访问控制规则,更灵活地设计和实现一些特殊的功能。
弊:
破坏封装性: 友元可能破坏了封装性,使得某些类的实现细节对外部代码可见,增加了代码的耦合性。
难以维护: 使用友元会增加代码的复杂性,降低代码的可读性和可维护性。当类的实现发生变化时,可能会影响到多个类和函数。
安全性问题: 友元提供了一定程度的权限,如果不慎使用,可能导致安全性问题,因为友元能够绕过正常的访问控制。
7.21
#include<iostream>
#include<string>
#include<queue>
using namespace std;
class Sales_data {
friend ostream& print(ostream& os, const Sales_data& item);
friend istream& read(istream& is, Sales_data& item);
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(p* n) {}
Sales_data(istream&);
std::string isbn() const { return bookNo; };
Sales_data& combine(const Sales_data&);
private:
int avg_price() const { return revenue / units_sold; }
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
istream& read(istream& is, Sales_data& item);
Sales_data::Sales_data(istream& is) {
read(is, *this);//这个read前面有,就不再贴定义了
}
//Sales_data的非接口成员函数
Sales_data add(const Sales_data& lhs, const Sales_data& rhs) {
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
ostream& print(ostream& os, const Sales_data& item) {
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.avg_price();
return os;
}
istream& read(istream& is, Sales_data& item) {
double price = 0;
//输入ISBN、售出总数和售出价格
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
int main()
{
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?!" << std::endl;
return -1;
}
return 0;
}
7.22
class Person {
public:
Person() = default;
Person(const string& n, const string& a) :name(n), address(a) {}
string get_name() const { return name; }
string get_address() const { return address; }
private:
string name;
string address;
};
7.23
#include <string>
class Screen {
public:
using pos = std::string::size_type;
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, width = 0;
std::string contents;
};
7.24
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) { }
Screen(pos ht, pos wd) :height(ht), width(wd), contents(ht* wd, ' ') { }
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, width = 0;
std::string contents;
};
7.25
可以,因为它的数据都是可以默认拷贝的类型,也没有指针
7.26
加个inline就行了,这就不写了
7.27
//这些都是写在类内部的
Screen &move(pos r,pos c){
pos row = r * width;
cursor = row + c;
return *this;
}
Screen& set(char c) {
contents[cursor] = c;
return *this;
}
Screen& set(pos r,pos col,char c) {
contents[r*width+col] = c;
return *this;
}
Screen& display(ostream& os) { do_display(os); return *this; }
const Screen &display(ostream &os) const{ do_display(os); return *this; }
7.28
两次display的显示内容不同。如果不加引用,第一次对myScreen的操作其实是对其一个副本的操作,myScreen的内容并没有改变,所以两次显示的内容不同且第二次display的还是原来myScreen的内容。
7.29
自行修改验证
7.30
优点:
- 明确性:使用
this
指针可以明确地指出你正在访问当前对象的成员,这可以帮助避免混淆或歧义。- 区分局部变量和成员变量:当成员变量的名称与局部变量相同时,使用
this
指针可以明确地指出你要访问的是成员变量而不是局部变量。- 在某些情况下,可以避免命名冲突:如果在类的成员函数中有多个变量具有相同的名称(例如,在不同的成员函数中参数名称相同),使用
this
指针可以帮助避免名称冲突。缺点:
- 繁琐:在每次访问成员变量时都使用
this
指针可能会使代码显得冗长,尤其是在简单的情况下,这样做可能会让代码难以阅读和理解。- 不必要的:在许多情况下,可以省略
this
指针而直接访问成员变量,特别是当没有名称冲突时。- 可读性降低:过度使用
this
指针可能会降低代码的可读性,因为它使代码更加冗长,而且可能会使代码难以理解。
7.31
class Y;
class X {
Y* ptr;
};
class Y {
X x;
};
7.32
class Screen;
class Window_mgr {
public:
using ScreenIndex = std::vector<Screen>::size_type;
void clear(ScreenIndex);
private:
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, char c) :height(ht), width(wd), contents(ht* wd, c) { }
Screen(pos ht, pos wd) :height(ht), width(wd), contents(ht* wd, ' ') { }
char get() const { return contents[cursor]; }
char get(pos r, pos c) const { return contents[r * width + c]; }
Screen &move(pos r,pos c){
pos row = r * width;
cursor = row + c;
return *this;
}
Screen& set(char c) {
contents[cursor] = c;
return *this;
}
Screen& set(pos r,pos col,char c) {
contents[r*width+col] = c;
return *this;
}
Screen& display(ostream& os) { do_display(os); return *this; }
const Screen &display(ostream &os) const{ do_display(os); return *this; }
private:
void do_display(ostream& os)const { os << contents; }
pos cursor = 0;
pos height = 0, width = 0;
std::string contents;
};
void Window_mgr::clear(ScreenIndex i) {
Screen& s = screens[i];
s.contents = string(s.height * s.width, ' ');
}
7.33
Screen::pos Screen::size() const {
return height * width;
}
7.34
会报错
7.35
在外面定义的setVal的Type是string,initVal则是类内部的函数
这么写会报错,需要把Type改成Exercise::Type
7.36
rem的初值改成i%j
7.37
next用了默认构造函数
last使用了Sales_data(const string& s) :bookNo(s) {}这个构造函数
7.38
Sales_data(istream& i=std::cin);
7.39
不合法,这样就不知道调用哪个默认构造函数了
7.40略
7.43
class NoDefault {
public:
NoDefault(int d):data(d){}
int data;
};
class C {
public:
C():member(1){}
NoDefault member;
};
7.44
不合法,因为NoDefault没有默认构造函数
7.45
合法,C有默认构造函数
7.46(答案不确定)
(a)不对,构造函数可以被删除
(b)不对,可以是有缺省参数的构造函数
(c)对
(d)不对,如果定义了其他构造函数则编译器不会为类生产默认的构造函数
7.47
这个看自己需求吧,如果使用explicit的话就不允许通过传递string类型来隐式构造Sales_data
7.48
是不是explicit都没有影响
7.49
a可运行
b不可运行,因为生成的Sales_data是一个临时对象,不能传递给引用
c也不可运行,因为i不是const类型的Sales_data
7.50略
7.51
如果vector的单参数不是explicit,那一个int数字都可以隐式转化成vector,这是很不合理的。
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(h),io(i),other(o){}
constexpr bool any() { return hw || io || other; }
void set_io(bool b) { io = b; }
void set_hw(bool b) { hw = b; }
void set_other(bool b) { other = b; }
private:
bool hw;
bool io;
bool other;
};
7.54
不可以,常量函数的唯一可执行语句就是返回语句,set_需要设置
7.55
不是,它的成员不都是字面值类型。
7.56
类的静态成员是属于类本身而不是类的实例的成员变量或成员函数。可以通过类名来访问静态成员,而不需要创建类的实例。静态成员在整个类的所有实例之间共享,它们的生命周期与程序的生命周期相同。
静态成员的优点包括:
共享数据:静态成员被类的所有实例共享,因此它们可以用来表示属于类的数据或状态,而不是特定于类的实例。
全局访问:可以直接通过类名来访问静态成员,无需创建类的实例。这使得静态成员成为了一个类的全局变量,可以在整个程序中方便地访问。
与类紧密关联:静态成员与类的实例无关,它们与类本身密切相关。这意味着它们可以用于表示类级别的属性或行为,而不是实例级别的。
静态成员与普通成员的区别包括:
内存分配:静态成员在程序启动时分配内存,直到程序结束才会释放,而普通成员在每个类的实例创建时分配内存,并在实例销毁时释放。
访问方式:静态成员可以直接通过类名来访问,而普通成员需要通过类的实例来访问。
生命周期:静态成员的生命周期与程序的生命周期相同,而普通成员的生命周期与所属的类的实例相关。
作用范围:静态成员是类级别的,所有的类实例共享同一个静态成员。而普通成员是实例级别的,每个类实例都有自己的一份普通成员数据。
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不可以,因为rate不是常量类型
vec不可以,因为vector也不是常量