本书为Primer C++ 中文第五版
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;
}
(7.2)
class Sales_data {
public:
std::string isbn() {return bookNo;}
Sales_data combine(const Sales_data &rhs) {
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
private:
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
(7.3)
见7.1
(7.4)
class People {
private:
std::string name ;
std::string addr ;
};
(7.5)
class People {
public:
std::string getName() const {return name;}
std::string getAddr() const {return addr;}
private:
std::string name ;
std::string addr ;
};
(7.6)
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 &item) {
double price = 0;
is >> item.bookNo >> item.units_sold >> price ;
item.revenue = price * item.units_sold;
return is;
}
ostream &print(ostream &os, const Sales_data &item) {
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue ;
return os;
}
(7.7)
见7.1
(7.8)
read 需要修改参数里面的变量,而print不需要改变变量
(7.9)
std::istream &read(std::istream &is, Person &people) {
is >> people.name >> people.addr;
return is;
}
std::ostream &print(std::ostream &os, const Person &people) {
os << people.name << people.addr <<"\n";
return os;
}
(7.10)
成功写入两个data
(7.11)
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(p * n) { }
(7.12)
Sales_data(std::istream &is) { read(is, *this); }
(7.13)
Sales_data item(cin);
(7.14)
Sales_data( const std::string &s) :
bookNo(s), units_sold(0), revenue(0) { }
(7.15)
People() = default;
People(std::string n) : name(n) {}
People(std::string n, std::string a) : name(n), addr(a) {}
(7.16)
公开出来可以被其他类可以使用的定义为public
封装起来只允许本身使用的定义为private
(7.17)
class 默认属性为private
struct 默认属性为public
(7.18)
(1)封装是指保护类的成员不被随意访问的能力。通过把类的实现细节设置为private,我们就能完成类的封装。封装实现了接口和实现的分离。
(2)封装有两个重要的优点:一是确保用户代码不会无意间破坏对象的状态;二是被封装的类的具体实现细节可以随时改变,而无须调整用户级别的代码。
只要类的接口不变,用户代码就无须改变。
(7.19)
构造函数和获取函数定义为public
姓名和地址被定义为private
(7.20)
当外部函数想要访问类内部的私有成员的时候,可以使用友元。
优点:可以使外部函数访问类的私有成员
缺点:会破坏类的封装性
(7.21)
(7.22)
上面的例子已经实现了封装
(7.23)
(7.24)
class Screen {
public:
typedef string::size_type pos;
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;
string contents;
};
(7.25)
能安全的依赖于默认版本。因为Screen类的内部成员都有默认的拷贝和赋值操作。
(7.26)
在类的内部定义的话,默认为内联函数。
在内的外部定义的话,需要加上inline关键字
eg.
inline double Sales_data::avg_price() { /* ... */}
(7.27)
//内联函数应该定义在头文件中
inline Screen &Screen::move(pos r, pos c) {
pos row = r * width;
cursor = row + c;
return *this;
}
inline Screen &Screen::set(char c) {
contents[cursor] = c;
return *this;
}
inline Screen const &Screen::display(ostream &os) const {
os<
#ifndef X_H
#define X_H
class Y;
class X {
public:
Y *y;
int getx() {return x;}
private:
int x = 0;
};
#endif
#ifndef Y_H
#define Y_H
#include "X.h"
class Y {
public:
X x;
int gety() {return y;}
private:
int y = 1;
};
#endif
#include
#include "Y.h"
int main() {
X x;
Y y;
std::cout<<"x "<
#ifndef WINDOW_MGR_H
#define WINDOW_MGR_H
#include "screen.h"
class Window_mgr {
public:
using ScreenIndex = vector::size_type;
void clear(ScreenIndex);
private:
vector screens{Screen(5, 115, 'w')};
};
void Window_mgr::clear(ScreenIndex i) {
Screen &s = screens[i];
s.contents = string(s.height * s.width, ' ');
}
#endif
class Window_mgr;
class Screen {
friend class Window_mgr;
/*
...
*/
};
pos未声明,应该修改为
Screen::pos Screen::size() const {
return height * width;
}
(7.34)
后面的 pos 未被声明
(7.35)
typedef string Type;// Type 为 string
Type initVal(); //Type 为 string, initVal 函数返回 string
class Exercise {
public:
typedef double Type;// Type 为 double
Type setVal(Type);// Type 为 duble
Type initVal();//Type 为 double, initVal 函数返回 double
private:
int val;
};
Exercise::Type Exercise::setVal(Type parm) { //第一个 Type 为 string 第二个 Type 为 double 出现错误
val = parm + initVal();//initVal 函数返回 double
return val;
}
修改为
Exercise::Type Exercise::setVal(Type parm) {
val = parm + initVal();
return val;
}
(7.36)
在构造函数中,变量的初始化顺序与变量的声明顺序一致。所以按照声明的顺序,首先初始化rem,此时,base尚未初始化,所以错误。应改为:
struct X {
X(int i, int j): base(i), rem(i * j) { }
int rem, base;
}
(7.37)
first_item(cin); // Sales_data(std::istream &is) { read(is, *this); }
next; //Sales_data() = default;
last;//Sales_data( const std::string &s) : bookNo(s) { }
(7.38)
Sales_data(std::istream &is = std::cin) { read(is, *this); }
(7.39)
不合法,会导致二义性
(7.40)
class Date {
public:
Date(int y, int m, int d, string ins)
: year(y), month(m), day(d), instructions(ins) { }
private:
int year, month, day;
string instructions;
};
(7.41)
受委托的构造函数先执行,然后再执行委托者的构造函数
(7.42)
class Date {
public:
Date(int y, int m, int d, string ins)
: year(y), month(m), day(d), instructions(ins) { }
Date(int y, int m, int d): Date(y, m, d, ' ') { } //委托构造函数
private:
int year, month, day;
string instructions;
};
(7.43)
class C {
public:
C(int i = 0): nd(i) { }
private:
NoDefault nd;
};
(7.44)
不合法,因为NoDefault没有默认构造函数
(7.45)
合法,C有默认构造函数
(7.46)
(a)错误,存在默认构造函数
(b)错误,默认构造函数也可以带参数,但必须有默认值
(c)错误,没有默认构造函数的话,在编译器需要隐式地使用时,会无法使用
(d)错误,内置类型和符合类型的成员只对定义在全局作用域的对象执行初始化
(7.47)
应该是explicit的,防止string隐式转换成Sales_data类型
(7.48)
string null_isbn("9-999-99999-9");//给null_isbn对象赋值
Sales_data item1(null_isbn);
Sales_data item2("9-999-99999-9");
无论是否是explicit,item1和item2都会调用Sales_data(string &s)被构造成功
(7.49)
当调用i.combine(s)时,编译器首先用给定的string对象s创建一个临时Sales_data对象
a>正确,生成的临时对象传给combine的形参,正确执行并返回结果
b>错误,因为combine的参数是非常量引用,但是形参是临时对象,不能正确引用
c>错误,尽管可以使用常量引用正确引用临时对象,但是combine函数作为常量函数,不能改变数据成员的值
(7.50)
当不希望出现隐式转换的时候需要加上explicit
explicit只能出现在参数为1个或者n个参数但有n-1个参数有默认值的时候
(7.51)
因为vector接受的单参数是int类型,这个参数的本义是容器的大小,不可隐式转换为容器。
(7.52)
聚合类
所有成员都是public
没有定义任何构造函数
没有类内初始值
没有基类,也没有virtual函数
初始化类中的所有数据成员,但是因为Sales_data的数据成员含有类内初始值,不满足条件
应改为:
Struct Sales_data {
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(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)
constexpr函数只能包含return语句,不允许执行其他任务
(7.55)
不是,因为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 double initRate() { interestRate = 1.0; };
static constexpr int period = 30;
double daily_tbl[period];
};
(7.58)
静态成员不应该在类的内部初始化,除了静态常量成员
所以 rate 和 vec 不能在类内初始化。