练习7.1:
#include<iostream>
#include<string>
using namespace std;
// Sale_data类
struct Sales_data
{
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
void AddData(Sales_data data);
void SetData(Sales_data data);
void PrintData();
};
void Sales_datas::AddData(Sales_data data)
{
if (bookNo != data.bookNo)
return;
units_sold += data.units_sold;
revenue += data.revenue;
}
void Sales_datas::SetData(Sales_data data)
{
bookNo = data.bookNo;
units_sold = data.units_sold;
revenue = data.revenue;
}
void Sales_datas::PrintData()
{
cout << bookNo << " " << units_sold << " " << revenue << " " << endl;
}
// 书店程序
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.AddData(trans);
}
else
{
total.PrintData(); // 输出前一本书的销售结果
total.SetData(trans); // 处理下一本书
}
}
total.PrintData(); // 打印最后一本书的结果
}
else
{
cerr << "No data?!" << endl;
return -1;
}
return 0;
}
练习7.2:
#include<iostream>
#include<string>
using namespace std;
struct Sales_data
{
string isbn() const { return this->bookNo; }
Sales_data& combine(const Sales_data& rhs);
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
this->units_sold += rhs.units_sold;
this->revenue += rhs.revenue;
return *this;
}
练习7.3:
#include"Sales_data.h"
int main()
{
Sale_data total;
if (cin >> total.bookNo >> total.units_sold >> total.revenue)
{
Sale_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
{
cerr << "No data?!" << endl;
return -1;
}
return 0;
}
练习7.4 && 练习7.5:
struct Person
{
// 这些函数应该是const,形参列表后面的const表示这是常量成员函数。
// 这些函数对调用它的对象的数据成员只读不写。
const string& getName() const { return this->name; }
const string& getAddress() const { return this->address; }
string name;
string address;
};
练习7.6:
#include<iostream>
#include<string>
using namespace std;
struct Sales_data
{
string isbn() const { return this->bookNo; }
Sales_data& combine(const Sales_data& rhs);
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
this->units_sold += rhs.units_sold;
this->revenue += rhs.revenue;
return *this;
}
// 不能返回引用,sum是局部对象
Sales_data add(const Sales_data& lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
// IO类属于不能被拷贝地类型,因此只能通过引用传递它们
// 读取和写入地操作会改变流地内容,所以两个函数接受地都是普通引用,而非对常量地引用
istream& read(istream& is, Sales_data& data)
{
double price = 0;
is >> data.bookNo >> data.units_sold >> price;
data.revenue = data.units_sold * price;
return is;
}
ostream& print(ostream& os, const Sales_data& data)
{
os << data.isbn() << " " << data.units_sold << " " << data.revenue;
return os;
}
练习7.7:
#include"Sales_data.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:
因为读操作改变了data的数据成员,而写操作并没有改变。
练习7.9:
#include<iostream>
#include<string>
using namespace std;
struct Person
{
const string& getName() const { return this->name; }
const string& getAddress() const { return this->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;
}
练习7.10:
read(cin, data1)返回cin的引用。if语句的条件是一次读取两个Sales_data对象。
练习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(n*p){}
Sales_data(istream&);
string isbn() const { return this->bookNo; }
Sales_data& combine(const Sales_data& rhs);
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
// 非成员函数
Sales_data add(const Sales_data& lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
istream& read(istream& is, Sales_data& data)
{
double price = 0;
is >> data.bookNo >> data.units_sold >> price;
data.revenue = data.units_sold * price;
return is;
}
ostream& print(ostream& os, const Sales_data& data)
{
os << data.isbn() << " " << data.units_sold << " " << data.revenue;
return os;
}
// 成员函数
Sales_data::Sales_data(istream& is)
{
read(is, *this);
}
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
this->units_sold += rhs.units_sold;
this->revenue += rhs.revenue;
return *this;
}
int main()
{
Sales_data data1;
print(cout, data1) << endl;
Sales_data data2("0-201-78345-X");
print(cout, data2) << endl;
Sales_data data3("0-201-78345-X", 3, 20.00);
print(cout, data3) << endl;
Sales_data data4(cin);
print(cout, data4) << endl;
return 0;
}
练习7.12:
#include<iostream>
#include<string>
using namespace std;
struct Sales_data;
istream& read(istream&, 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(istream& is) { read(is, *this); }
string isbn() const { return this->bookNo; }
Sales_data& combine(const Sales_data& rhs);
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
// 非成员函数
Sales_data add(const Sales_data& lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
istream& read(istream& is, Sales_data& data)
{
double price = 0;
is >> data.bookNo >> data.units_sold >> price;
data.revenue = data.units_sold * price;
return is;
}
ostream& print(ostream& os, const Sales_data& data)
{
os << data.isbn() << " " << data.units_sold << " " << data.revenue;
return os;
}
// 成员函数
Sales_data::Sales_data(istream& is)
{
read(is, *this);
}
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
this->units_sold += rhs.units_sold;
this->revenue += rhs.revenue;
return *this;
}
int main()
{
Sales_data data1;
print(cout, data1) << endl;
Sales_data data2("0-201-78345-X");
print(cout, data2) << endl;
Sales_data data3("0-201-78345-X", 3, 20.00);
print(cout, data3) << endl;
Sales_data data4(cin);
print(cout, data4) << endl;
return 0;
}
练习7.13:
#include"Sales_data.h"
int main()
{
Sales_data total(cin);
if (!total.isbn().empty())
{
istream& is = cin;
while (is)
{
Sales_data trans(is);
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.14:
Sales_data() : units_sold(0), revenue(0){}
练习7.15:
#include<iostream>
#include<string>
using namespace std;
struct Person;
istream& read(istream&, Person&);
struct Person
{
Person() = default;
Person(const string& sname, const string& saddress):
name(sname), address(saddress){}
Person(istream& is) { read(is, *this); }
const string& getName() const { return this->name; }
const string& getAddress() const { return this->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;
}
练习7.16:
对于访问说明符出现的位置和次数没有限制。指定的访问级别将一直有效,直到下一个访问说明符或类主体的结束。
程序的所有部分都可以访问的成员应该定义在public说明符之后。类的成员函数可以访问但使用类的代码不能访问的成员应该定义在private说明符之后。
练习7.17:
使用class和使用struct的唯一区别是默认访问权限不一样(class: private, struct: public)
练习7.18:
封装是实现与接口的分离。它隐藏了类型的实现细节。(在c++中,封装是通过将实现放在类的private部分来实现的)
练习7.19:
public:构造函数和成员函数: getName(), getAddress().
private:数据成员: name, address.
接口应该定义为public,数据不应该公开给类外部。
练习7.20:
友元可以允许其他类或者函数访问它的非公有成员。
优点:
可以在类作用域中引用类成员,而不需要显式地在它们前面加上类名。
可以方便地访问所有非公共成员。
有时,对类的用户来说可读性更强。
缺点:
减少了封装,从而降低了可维护性。
代码冗长,类内声明,类外声明。
练习7.21:
#include<iostream>
#include<string>
using namespace std;
struct Sales_data
{
friend istream& read(istream& is, Sales_data& data);
friend ostream& print(ostream& os, const Sales_data& data);
friend Sales_data add(const Sales_data& lhs, const Sales_data& rhs);
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(istream&);
string isbn() const { return this->bookNo; }
Sales_data& combine(const Sales_data& rhs);
private:
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
Sales_data add(const Sales_data& lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
istream& read(istream& is, Sales_data& data)
{
double price = 0;
is >> data.bookNo >> data.units_sold >> price;
data.revenue = data.units_sold * price;
return is;
}
ostream& print(ostream& os, const Sales_data& data)
{
os << data.isbn() << " " << data.units_sold << " " << data.revenue;
return os;
}
Sales_data::Sales_data(istream& is)
{
read(is, *this);
}
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
this->units_sold += rhs.units_sold;
this->revenue += rhs.revenue;
return *this;
}
练习7.22:
#include<iostream>
#include<string>
using namespace std;
struct Person;
istream& read(istream&, Person&);
struct Person
{
friend istream& read(istream& is, Person& person);
friend ostream& print(ostream& os, const Person& person);
public:
Person() = default;
Person(const string& sname, const string& saddress) :
name(sname), address(saddress) {}
Person(istream& is) { read(is, *this); }
const string& getName() const { return this->name; }
const string& getAddress() const { return this->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 && 练习7.24:
#include<iostream>
#include<string>
using namespace std;
class Screen
{
public:
using pos = 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]; } // 获取光标处的字符
inline char get(pos r, pos c) const;
Screen& move(pos r, pos c);
private:
pos cursor = 0;
pos height = 0, width = 0;
string contents;
};
char Screen::get(pos r, pos c) const
{
return contents[r * width + c];
}
inline
Screen& Screen::move(pos r, pos c)
{
cursor = r * width + c;
return *this;
}
练习7.25:
当类需要分配类对象之外的资源时,合成的版本常常会失效。如果类包含vector或者string成员,则其拷贝,赋值和销毁的合成版本能够正常工作。因此只使用内置类型和string的Screen类可以依赖于拷贝和赋值操作的默认版本。
练习7.26:
class Sales_data {
public:
// ...
private:
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;
}
练习7.27:
#include<iostream>
#include<string>
using namespace std;
class Screen
{
public:
using pos = 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]; } // 获取光标处的字符
inline char get(pos r, pos c) const;
Screen& move(pos r, pos c);
Screen& set(char ch);
Screen& set(pos r, pos c, 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 << contents; }
pos cursor = 0;
pos height = 0, width = 0;
string contents;
};
char Screen::get(pos r, pos c) const
{
return contents[r * width + c];
}
inline
Screen& Screen::move(pos r, pos c)
{
cursor = r * width + c;
return *this;
}
inline
Screen& Screen::set(char ch)
{
contents[cursor] = ch;
return *this;
}
inline
Screen& Screen::set(pos r, pos c, char ch)
{
contents[r * width + c] = ch;
return *this;
}
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:
调用display无法在输出中打印#,定义的返回类型不是引用,导致调用move的返回值将是*this的副本,因此调用set只能更改临时副本,而不能改变myScreen的值。
练习7.29:
# with '&'
XXXXXXXXXXXXXXXXXXXX#XXXX
XXXXXXXXXXXXXXXXXXXX#XXXX
# without '&'
XXXXXXXXXXXXXXXXXXXX#XXXX
XXXXXXXXXXXXXXXXXXXXXXXXX
练习7.30:
优点:
- 更明确
- 更少的误读
- 可以使用名称与成员名称相同的成员函数参数。
void setAddress(const string &address) {this->address = address;}
缺点:
- 更多的阅读
- 有时冗余
string getAddr() const { return this->addr; }
练习7.31:
class Y;
class X
{
Y* y;
};
class Y
{
X x;
};
练习7.32:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
class Screen;
class Window_mgr
{
public:
using ScreenIndex = vector<Screen>::size_type;
// 按照编号将指定的Screen重置为空白
void clear(ScreenIndex);
private:
vector<Screen> screens{ Screen(24, 80, ' ') };
};
class Screen
{
friend void Window_mgr::clear(ScreenIndex);
public:
using pos = 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;
Screen& move(pos r, pos c);
Screen& set(char ch);
Screen& set(pos r, pos c, 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 << contents; }
pos cursor = 0;
pos height = 0, width = 0;
string contents;
};
inline
void Window_mgr::clear(ScreenIndex i)
{
if (i >= screens.size()) return;
// s是一个Screen的引用,指向我们想清空的那个屏幕
Screen& s = screens[i];
// 将选定的Screen重置为空白
s.contents = string(s.height * s.width, ' ');
}
inline
char Screen::get(pos r, pos c) const
{
return contents[r * width + c];
}
inline
Screen& Screen::move(pos r, pos c)
{
cursor = r * width + c;
return *this;
}
inline
Screen& Screen::set(char ch)
{
contents[cursor] = ch;
return *this;
}
inline
Screen& Screen::set(pos r, pos c, char ch)
{
contents[r * width + c] = ch;
return *this;
}
练习7.33:
Screen::pos Screen::size() const
{
return height * width;
}
练习7.34:
pos没有被声明。pos必须先定义后使用。
练习7.35:
typedef string Type;
Type initVal(); // use "string"
class Exercise {
public:
typedef double Type;
Type setVal(Type); // sue "double"
Type initVal(); // use "double"
private:
int val;
};
Type Exercise::setVal(Type parm) { // first is "string", second is "double"
val = parm + initVal(); // Exercise::initVal()
return val;
}
// 修改
Exercise::Type Exercise::setVal(Type parm) {
val = parm + initVal();
return val;
}
练习7.36:
在这种情况下,构造函数的初始化式让它看起来像是用i初始化了base,然后用base初始化了rem。这个初始化式的效果是用未定义的base值初始化rem !
struct X {
X (int i, int j): base(i), rem(base % j) { }
int base, rem;
};
练习7.37:
Sales_data first_item(cin); // use Sales_data(std::istream &is) ; its value are up to your input.
int main() {
Sales_data next; // use Sales_data(string s = "");
// bookNo = "", units_sold = 0, revenue = 0.0
Sales_data last("9-999-99999-9"); // use Sales_data(std::string s = "");
// bookNo = "9-999-99999-9", units_sold = 0, revenue = 0.0
}
练习7.38:
Sales(istream& is = cin)
{
read(is, *this);
}
练习7.39:
非法的。因为调用重载的"Sales_data()"是二义性的。
练习7.40:
class Book
{
public:
Book() = default;
Book(string bookNo, string name, string author):
m_BookNO(bookNo), m_Name(name), m_Author(author){}
Book(istream& is) { is >> m_BookNO >> m_Name >> m_Author; }
private:
string m_BookNO;
string m_Name;
string m_Author;
};
练习7.41:
头文件: Sales_data.h
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include<iostream>
#include<string>
using namespace std;
struct Sales_data
{
friend istream& read(istream& is, Sales_data& data);
friend ostream& print(ostream& os, const Sales_data& data);
friend Sales_data add(const Sales_data& lhs, const Sales_data& rhs);
public:
Sales_data(const string& s, unsigned n, double p) :
bookNo(s), units_sold(n), revenue(n* p)
{
cout << "Sales_data(const string&, unsigned, double)" << endl;
}
Sales_data() : Sales_data("", 0, 0.0f)
{
cout << "Sales_data()" << endl;
}
Sales_data(const string& s) : Sales_data(s, 0, 0.0f)
{
cout << "Sales_data(const string&)" << endl;
}
Sales_data(istream&);
string isbn() const { return this->bookNo; }
Sales_data& combine(const Sales_data& rhs);
private:
inline double avg_price() const;
private:
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
inline double Sales_data::avg_price() const
{
return units_sold ? revenue / units_sold : 0;
}
#endif
头文件: Sales_data.cpp
#include"Sales_data.h"
Sales_data::Sales_data(istream& is) : Sales_data()
{
cout << "Sales_data(istream &is)" << endl;
read(is, *this);
}
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
this->units_sold += rhs.units_sold;
this->revenue += rhs.revenue;
return *this;
}
Sales_data add(const Sales_data& lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
istream& read(istream& is, Sales_data& data)
{
double price = 0;
is >> data.bookNo >> data.units_sold >> price;
data.revenue = data.units_sold * price;
return is;
}
ostream& print(ostream& os, const Sales_data& data)
{
os << data.isbn() << " " << data.units_sold << " " << data.revenue;
return os;
}
main文件:
#include"Sales_data1.h"
int main()
{
cout << "1. default way: " << endl;
cout << "----------------" << endl;
Sales_data s1;
cout << "\n2. use 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(cin);
return 0;
}
练习7.42:
class Book
{
public:
Book(string bookNo, string name, string author):
m_BookNO(bookNo), m_Name(name), m_Author(author){}
Book() : Book("", "", ""){}
Book(istream& is) { is >> m_BookNO >> m_Name >> m_Author; }
private:
string m_BookNO;
string m_Name;
string m_Author;
};
练习7.43_45:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
class NoDefault
{
public:
NoDefault(int i){}
};
class C
{
public:
C() : def(0) {}
private:
NoDefault def;
};
int main()
{
//vector<NoDefault> vec(10); // 非法: NoDefault没有合适的默认构造函数可用
vector<C> vec2(10); // 合法: C有默认构造函数
return 0;
}
练习7.46:
(a) 不正确。编译器会生成构造函数,称为合成的默认构造函数。
(b) 不正确,默认构造函数是在未提供初始值设定项时使用的构造函数。此外,为其所有参数提供默认参数的构造函数也定义了默认构造函数。
(c) 不正确,类应该提供。
(d) 不正确,只有当我们的类没有显式定义任何构造函数时,编译器才会隐式定义我们的默认构造函数。
练习7.47:
是否需要从string到sales_data的转换依赖于我们对用户使用该转换的看法。在此例中,这种转化可能是对的。null_book中的string可能表示了一个不存在的ISBN编号。
优点:
- 防止在需要隐式转换的上下文中使用构造函数
- 可以定义一个只能使用直接初始化形式的构造函数
缺点:
- 只对单参数调用的构造函数有意义
练习7.48:
两者都是调用构造函数直接初始化,没有发生隐式类型转换。
练习7.49:
(a)合法。用s自动创建一个临时Sales_data对象,传递给combine.
(b)不合法。因为combine的形参是非const引用,所以不能向该形参传递临时值。如果combine的形参是对const的引用,则可以向该形参传递一个临时值。
(c)不合法。参数列表后边的const禁止对数据成员进行任何更改,这与语义冲突。
练习7.50:
explicit Person(istream& is) { read(is, *this); }
练习7.51:
// 如果vector没有将其单参数构造函数定义为explicit。我们可以像这样使用这个函数:
int getSize(const vector<int>&);
getSize(34); // 不明确
// 字符串是不同的。通常,我们使用string来代替const char *。当我们这样调用一个函数时:
void setYourName(string);
setYourName("pezy"); // 明确
练习7.52:
如果我们想通过提供一个带括号的成员初始值列表来初始化数据成员,那么Sales_data应该是一个聚合类:
struct Sales_data {
string bookNo;
unsigned units_sold;
double revenue;
};
练习7.53:
#ifndef DEBUG_H
#define DEBUG_H
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_hw(bool b) { hw = b; }
void set_io(bool b) { io = b; }
void set_other(bool b) { other = b; }
private:
bool hw; // 硬件错误,而非IO错误
bool io; // IO错误
bool other; // 其他错误
};
#endif
练习7.54:
不应该,因为constexpr函数必须只包含一条return语句。
练习7.55:
不是,因为string不是一个字面值类型。
#include<iostream>
#include<string>
#include <type_traits>
using namespace std;
struct Data {
int ival;
string s;
};
int main()
{
cout << boolalpha;
cout << "Data: " << is_literal_type<Data>::value << endl;
cout << "string: " << is_literal_type<string>::value << endl;
return 0;
}
练习7.56:
类的静态成员是与类本身直接相关而不是与类的各个对象保持关联。
优点:每个对象不需要存储一个共同的数据,如果数据被更改,每个对象都可以使用新的值。
静态成员与普通成员的区别:
- 静态数据成员可以是不完全类型。
- 可以使用静态成员作为默认参数。
练习7.57:
#ifndef ACCOUNT_H
#define ACCOUNT_H
#include<string>
class Account
{
public:
void calculate() { amount += amount * interestRate; }
// 通常情况下。类的静态成员不应该在类的内部初始化。然而,我们可以为静态成员提供
// const整数类型的类内初始值,不过要求静态成员必须是字面值常量类型的constexpr。
static double rate() { return interestRate; }
static void rate(double);
private:
string owner;
double amount;
static double interestRate;
static constexpr double todayRate = 42.42; // todayRate是常量表达式
static double initRate() { return todayRate; }
};
// 不能重复static关键词,static只出现在类内部的声明语句
void Account::rate(double newRate)
{
interestRate = newRate;
}
double Account::interestRate = initRate();
#endif
练习7.58:
static double rate = 6.5;
^
rate必须是常量表达式
static vector<double> vec(vecSize);
^
vector不是字面值常量类型的constexpr
修改:
// 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);