第七章 类
练习7.1
@!#%^#()@(#(@#@@!#%%%#%@%))
练习7.2
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include <string>
struct Sales_data {
std::string isbn() const { return bookNo; }
Sales_data &combine (const Sales_data&);
std::string bookNo;
unsigned units_sold;
double revenue;
};
Sales_data& Sales_data::combine (const Sales_data& rhs) {
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return * this;
}
#endif // SALES_DATA_H
练习7.3
#include "Sales_data.h"
#include <iostream>
using std::cin;
using std::cout;
using std::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.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?!" << std::endl;
return -1;
}
return 0;
}
练习7.4
#ifndef PERSON_H_
#define PERSON_H_
#include <string>
struct Person {
std::string name;
std::string addr;
};
#endif // PERSON_H_
练习7.5
#ifndef PERSON_H_
#define PERSON_H_
#include <string>
struct Person {
std::string retName() const { return name; };
std::string retAddr() const { return addr; };
std::string name;
std::string addr;
};
#endif // PERSON_H_
练习7.6
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;
}
练习7.7
#include "Sales_data.h"
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main()
{
Sales_data total;
if (read(cin, total)) {
Sales_data trans;
while (read(cin, total)) {
if (total.isbn() == trans.isbn())
total.combine(trans);
else {
print(cout, total) << endl;
total = trans;
}
}
print(cout, total) << endl;
}
else {
std::cerr << "No data?!" << endl;
return -1;
}
return 0;
}
练习7.8
read 函数会改变 Sales_data 参数的值,所以定义成普通引用。
print 函数不存在对该参数的值的操作,所以定义成常量引用。
练习7.9
#include <iostream>
std::istream& readP(std::istream& is, Person& person) {
is >> person.name >> person.addr;
return is;
}
std::ostream& printP(std::ostream& os, const Person& person) {
os << person.name << " " << person.addr;
return os;
}
练习7.10
一次读入两项指定类型的数据。
练习7.11
//Sales_data.h红添加的部分
struct Sales_data {
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&);
};
Sales_data::Sales_data(std::istream& is) {
read(std::cin, *this);
}
//主函数
#include "Sales_data.h"
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main()
{
Sales_data in1;
print(cout, in1) << endl;
Sales_data in2("sup");
print(cout, in2) << endl;
Sales_data in3("hola", 5, 7.6);
print(cout, in3) << endl;
Sales_data in4(cin);
print(cout, in4) << endl;
return 0;
}
练习7.12
//由于 read 在类外定义,所以应该将 read 函数在类之前先声明一下,改变部分如下
struct Sales_data;
std::istream& read(std::istream&, Sales_data&);
struct Sales_data {
Sales_data(std::istream& is) {
read(std::cin, * this);
};
}
练习7.13
#include "Sales_data.h"
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main()
{
Sales_data total(cin);
if (!total.isbn().empty()) {
// std::istream& is = std::cin;
while (cin) {
Sales_data trans(cin);
if (total.isbn() == trans.isbn())
total.combine(trans);
else {
print(cout, total) << endl;
total = trans;
}
}
}
else {
std::cerr << "No data?!" << endl;
return -1;
}
return 0;
}
练习7.14
Sales_data() : units_sold(0), revenue(0.0) {}
练习7.15
Person() = default;
Person(std::string n) : name(n) {}
练习7.16
没有限制。
用来作为接口的构造函数与一些成员函数定义在 public 之后。
只能被类的成员函数访问的函数与类的成员定义在 private 之后。
练习7.17
有区别。默认访问权限不一样。
练习7.18
封装可以使类中的一部分成员不被使用该类的代码访问,增强了稳定性。
练习7.19
我会把 name 和 addr 声明成 private,这两个成员不应该被外界直接访问。
其他的成员函数可以声明成 public。
练习7.20
友元在类外函数想要访问声明成 private 的类成员时有用。
练习7.21
//修改的部分
class Sales_data {
friend Sales_data add(const Sales_data&, const Sales_data&);
friend std::istream& read(std::istream&, Sales_data&);
friend std::ostream& print(std::ostream&, const Sales_data&);
// consturcters
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(std::cin, *this);
}
std::string isbn() const { return bookNo; }
Sales_data &combine (const Sales_data&);
private:
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
练习7.22
class Person {
friend std::istream& readP(std::istream&, Person&);
friend std::ostream& printP(std::ostream&, const Person&);
public:
Person() = default;
Person(std::string n) : name(n) {};
std::string retName() const { return name; };
std::string retAddr() const { return addr; };
private:
std::string name;
std::string addr;
};
std::istream& readP(std::istream& is, Person& person) {
is >> person.name >> person.addr;
return is;
}
std::ostream& printP(std::ostream& os, const Person& person) {
os << person.name << " " << person.addr;
return os;
}
练习7.23 & 练习7.24
#ifndef SCREEN_H
#define SCREEN_H
class Screen {
public:
using pos = std::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() cosnt { return content[cursor]; }
inline char get(pos, pos) const;
private:
pos height = 0;
pos width = 0;
pos cursor = 0;
std::string content;
};
char Screen::get (pos row, pos w=col) {
return content[row * width + col];
}
#endif // SCREEN_H
练习7.25
可以。因为 Screen 中的成员都是内置类型与 string 类型,所以能安全地使用拷贝与赋值操作的默认版本。
练习7.26
!@%#^!#%&$!^#
练习7.27
#ifndef SCREEN_H
#define SCREEN_H
#include <string>
class Screen {
public:
using pos = std::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[cursor]; }
char get(pos, pos);
inline char get(pos, pos) const;
Screen &move(pos, pos);
Screen &set(char);
Screen &set(pos, pos, char);
Screen &display(std::ostream &os){ do_display(os); return * this;}
const Screen &display(std::ostream &os) const { do_display(os); return * this;}
private:
void do_display(std::ostream &os) const { os << content; }
pos height = 0;
pos width = 0;
pos cursor = 0;
std::string content;
};
char Screen::get (pos row, pos col) {
return content[row * width + col];
}
inline Screen &Screen::move(pos r, pos c){
cursor = r * width + c;
return * this;
}
inline Screen &Screen::set(char c) {
content[cursor] = c;
return * this;
}
inline Screen &Screen::set(pos row, pos col, char c) {
content[row * width + col] = c;
return * this;
}
#endif // SCREEN_H
练习7.28
第二行的输出与第一行不一样,而是与初始化时的 myScreen 一样。
练习7.29
正确。因为函数返回 Screen 导致这三个操作的 myScreen 本身无影响。
练习7.30
使用 this 指针通常能增加可读性,使代码更易被理解,但是也增加了代码量,显得多余。
练习7.31
class X;
class Y;
class X {
Y* y = nullptr;
};
class Y {
X x;
};
练习7.32
// 只包括新添加的部分
class Window_mgr {
public:
using ScreenIndex = std::vector<Screen>::size_type;
void clear(ScreenIndex);
private:
std::vector<Screen> screens;
};
class Screen {
friend void Window_mgr::clear(ScreenIndex);
};
void Window_mgr::clear(ScreenIndex i) {
Screen &s = screens[i];
s.content = string(s.height * s.width, ' ');
}
练习7.33
//pos 是在 Screen 内定义的数据类型
Screen::pos Screen::size() const {
return height * width;
}
练习7.34
之前有用到 pos 的地方都会报错。
练习7.35
typedef string Type;
Type initVal(); // Type 用的是 string 型
class Exercise {
public:
typedef double Type;
Type setVal(Type); // 用的是 double 型
Type initVal(); // 同上
private:
int val;
};
//需要修改,因为返回的 val 是 int 型,按上下文,这个的 Type 应该是类中声明的 Type
Exercise::Type Exercise::setVal(Type parm) { // 修改之前,前一个是 string 型,后一个是 double 型
val = parm + initVal(); // 用的 double 型
return val;
}
练习7.36
初始化顺序有误,应该按照声明的顺序先初始化 rem 再初始化 base。
练习7.37
对于 first_item ,他的成员取决于输入。
对于 next , 进行了默认初始化, bookNo = “”, units_sold = 0, revenue = 0.0。
对于 last , 入了只接受一个 string 实参的函数, bookNo = “9-999-99999-9”,
units_sold = 0, revenue = 0.0。
练习7.38
Sales_data(std::istream &is = std::cin) { read(is, *this); }
练习7.39
不合法,那样会使下文中调用函数时混淆。
练习7.40
%%^@ ##^&%&^*
练习7.41
//构造函数标记部分如下
Sales_data(const std::string& s, unsigned n, double p):
bookNo(s), units_sold(n), revenue(n * p) { std::cout << "core" << std::endl;}
Sales_data() : Sales_data("", 0, 0) { std::cout << "default" << std::endl;}
Sales_data(const std::string& s ) : Sales_data(s, 0, 0) { std::cout << "onlystring" << std::endl;}
Sales_data(std::istream& is) : Sales_data() {
std::cout << "all inoput" << std::endl;
read(is, *this);
}
//主函数部分
int main()
{
Sales_data item1("sada", 213, 213);
cout << "------------------" << endl;
Sales_data item2();
cout << "------------------" << endl;
Sales_data item3("asfasf");
cout << "------------------" << endl;
Sales_data item4(cin);
cout << "------------------" << endl;
}
练习7.42
(&……)@&@&)&#——&……
练习7.43
class C {
NoDefault c;
public:
C() : c(0) {} //默认构造函数
};
练习7.44
不合法,因为容器一开始有10个元素,都应该被初始化,而 NoDefault 类型没有默认构造函数。
练习7.45
合法。因为类型 C 中包含默认构造函数。
练习7.46
(a) 不正确。如果没有显式地定义构造函数,编译器会隐式地定义一个默认构造函数。
(b) 不正确。默认构造函数只是说可以不提供实参也能被调用,也有参数列表不空的默认构造函数。
(c) 不正确。也应该提供默认构造函数为特殊情况着想,比如在其他类中定义声明该类的成员时。
(d) 不正确。 只有在类中没有显式地定义任何构造函数时,编译器才会生成一个默认构造函数。
练习7.47
这样的定义没有太大的意义,但在不同的使用场景中,可能会有不一样要求。
练习7.48
item1 能被正常初始化, item2 不能。
这三行定义与构造函数是不是 explicit 无关。
练习7.49
(a) 正确。
(b) 不正确。因为 sting 型始终不能通过一次隐式转换成 Sales_data& 型。
(c) 声明有误,最后的 const 会导致指向 i 的 this 指针成为一个指向常量的指针,有违函数本身的意图。
练习7.50
没得必要。
练习7.51
容器本身里面的参数类型是需要被定义的,很难进行默认的隐式转换,而 string 型中每个元素的类型都是确定的。
练习7.52
存在问题,因为该 Sales_data 类中包含了类内初始值,所以不能这样初始化。
练习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_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;
};
#endif // DEBUG_H
练习7.54
不能吧。 constexpr 函数要求函数体中有且仅有一条 return 语句,而这三个 set 函数都是 void 型的。
练习7.55
不是。 string 类型不是字面值类型。
练习7.56
仅与类的本身相关而与类中的其他对象无关的成员叫做静态成员。它存在与存在与任何对象之外,所有该类的对象也共享这个静态成员。
练习7.57
#ifndef ACCOUNT_H_INCLUDED
#define ACCOUNT_H_INCLUDED
#include <string>
class Account{
public:
void calculate() { amount += amount * interestRate; }
static double rate() { return interestRate; }
static void rate(double);
private:
std::string owner;
double amount;
static double interestRate;
static double initRate();
};
void Account::rate(double newRate) {
interestRate = newRate;
}
double Account::rate(0.668);
double Account::interestRate;
#endif // ACCOUNT_H_INCLUDED
练习7.58
//example.h
class Example {
public:
static constexpr double rate = 6.5; //类内初始值需为字面值常量类型的 constexpr
static const int vecSize = 20;
static vector<double> vec; //不能在类内初始化
};
//example.C
#include "example.h"
constexpr double Example::rate;
std::vector<double> Example::vec(Example::vecSize);