下面的练习别忘记都加上下面这一语句
#include
为了使自己的程序有很好的移植性,c++程序员应该尽量使用size_t和size_type而不是int, unsigned
size_t是全局定义的类型;size_type是STL类中定义的类型属性,用以保存任意string和vector类对象的长度
string::size_type 制类型一般就是unsigned int, 但是不同机器环境长度可能不同 win32 和win64上长度差别;size_type一般也是unsigned int
使用的时候可以参考: string::size_type a =123; vector
size_type b=234; size_t b=456;size_t 使用的时候头文件需要
;size_type 使用的时候需要或者sizeof(string::size_type)
sizeof(vector::size_type)
sizeof(vector::size_type)
sizeof(size_t)
上述长度均相等,长度为win32:4 win64:8二者联系:在用下标访问元素时,vector使用vector::size_type作为下标类型,而数组下标的正确类型则是size_t
第二章练习2.41
在源文件处新建一个头文件
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include
using namespace std;
struct Sales_data
{
string bookNo; //bookNo被初始化为空字符串,书籍名字
unsigned units_sold = 0; //初始化0,销售额
double revenue = 0.0; //初始化0,平均价格
//一个isbn成员函数,用于返回对象的ISBN编号
string isbn() const { return bookNo; }
//一个combine成员函数,用于将一个Sales_data对象加到另一个对象上
Sales_data& combine(const Sales_data&);
};
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
#endif
#include"Sales_data.h"
using namespace std;
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 {
cerr << "NO data?!" << endl;
}
}

#ifndef PERSON_H
#define PERSON_H
#include
using namespace std;
struct Person
{
string name; //人员姓名
string address; //住址
};
#endif
应该是const,这两个成员函数只需读取成员对象,无需改变成员对象。
#ifndef PERSON_H
#define PERSON_H
#include
using namespace std;
struct Person
{
string name; //人员姓名
string address; //住址
string get_name() const { return name; }
string get_address() const { return address; }
};
#endif
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include
using namespace std;
struct Sales_data
{
string bookNo; //bookNo被初始化为空字符串,书籍名字
unsigned units_sold = 0; //初始化0,销售额
double revenue = 0.0; //初始化0,平均价格
//一个isbn成员函数,用于返回对象的ISBN编号
string isbn() const { return bookNo; }
//一个combine成员函数,用于将一个Sales_data对象加到另一个对象上
Sales_data& combine(const Sales_data&);
};
Sales_data& Sales_data::combine(const Sales_data& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
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 << " " << item.avg_price();
return os;
}
Sales_data add(const Sales_data& lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
#endif
#include
#include"Sales_data.h"
using namespace std;
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) << endl;
total = trans;
}
}
print(cout, total) << endl;
}
else {
cerr << "No data?!" << endl;
}
}
因为read函数需要改变成员对象;而print只需读取成员对象
#ifndef PERSON_H
#define PERSON_H
#include
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& item)
{
is >> item.name >> item.address;
return is;
}
ostream& print(ostream& os, Person const& item)
{
os << item.name << " " << item.address << " ";
return os;
}
#endif
读入data1和data2,并判断返回是否为真
if(read(read(cin,data1),data2))
头文件
#pragma once
#ifndef SALES_DATA_H
#define SALES_DATA_H
//练习7.6 对于函数add、read和print,定义你自己的版本
#include
#include
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&);
string bookNo; //bookNo被初始化为空字符串,书籍名字
unsigned units_sold = 0; //初始化0,销售额
double revenue = 0.0; //初始化0,销售总价
//一个isbn成员函数,用于返回对象的ISBN编号
string isbn() const { return bookNo; }
//一个combine成员函数,用于将一个Sales_data对象加到另一个对象上
Sales_data& combine(const Sales_data&);
//用于求平均价格
double avg_price() const; //平均价格
};
Sales_data& Sales_data::combine(const Sales_data& rhs) //把后加入的书销售额和销售总价加到上一本书中(同名)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
double Sales_data::avg_price() const //平均价格
{
if (units_sold) {
return revenue / units_sold;
}
else {
return 0;
}
}
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 << " " << item.avg_price();
return os;
}
Sales_data add(const Sales_data& lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
//内联自定义默认构造函数
Sales_data::Sales_data(istream& is)
{
read(is, *this);
}
#endif
主函数
#include
#include"Sales_data.h"
using namespace std;
int main()
{
Sales_data sales_data1;
print(cout, sales_data1) << endl;
Sales_data sales_data2("1-01");
print(cout, sales_data2) << endl;
Sales_data sales_data3("1-01", 1, 100);
print(cout, sales_data3) << endl;
Sales_data sales_data4(cin);
print(cout, sales_data4) << endl;
// Sales_data sales_data5();
// print(cout, sales_data5) << endl;
return 0;
}

头文件
#pragma once
#ifndef SALES_DATA_H
#define SALES_DATA_H
//练习7.6 对于函数add、read和print,定义你自己的版本
#include
#include
using namespace std;
struct Sales_data;
istream& read(std::istream& is, Sales_data& item);
ostream& print(std::ostream& os, const Sales_data& item);
Sales_data add(const Sales_data& lhs, const Sales_data& rhs); //先声明
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); }
string bookNo; //bookNo被初始化为空字符串,书籍名字
unsigned units_sold = 0; //初始化0,销售额
double revenue = 0.0; //初始化0,销售总价
//一个isbn成员函数,用于返回对象的ISBN编号
string isbn() const { return bookNo; }
//一个combine成员函数,用于将一个Sales_data对象加到另一个对象上
Sales_data& combine(const Sales_data&);
//用于求平均价格
double avg_price() const; //平均价格
};
Sales_data& Sales_data::combine(const Sales_data& rhs) //把后加入的书销售额和销售总价加到上一本书中(同名)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
double Sales_data::avg_price() const //平均价格
{
if (units_sold) {
return revenue / units_sold;
}
else {
return 0;
}
}
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 << " " << item.avg_price();
return os;
}
Sales_data add(const Sales_data& lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
#endif
主函数
#include
#include"Sales_data.h"
using namespace std;
int main()
{
Sales_data sales_data1;
print(cout, sales_data1) << endl;
Sales_data sales_data2("1-01");
print(cout, sales_data2) << endl;
Sales_data sales_data3("1-01", 1, 100);
print(cout, sales_data3) << endl;
Sales_data sales_data4(cin);
print(cout, sales_data4) << endl;
// Sales_data sales_data5();
// print(cout, sales_data5) << endl;
return 0;
}

#include
#include"Sales_data.h"
using namespace std;
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) << endl;
total = trans;
}
}
print(cout, total) << endl;
}
else {
cerr << "No data?!" << endl;
}
}

Sales_data() : bookNo(""), units_sold(0), revenue(0) { }
#ifndef PERSON_H
#define PERSON_H
#include
#include
using namespace std;
istream& read(istream& is, Person& item);
ostream& print(ostream& os, Person const& item);
struct Person
{
//内联自定义默认构造函数
Person(const string& s) :name(s) {} //可以单独输入一个name
Person(const string& s1, const string& s2) :name(s1), address(s2) {} //可以输入name和address
Person(istream& is) { read(is, *this); }
string name; //人员姓名
string address; //住址
string get_name() const { return name; }
string get_address() const { return address; }
};
istream& read(istream& is, Person& item)
{
is >> item.name >> item.address;
return is;
}
ostream& print(ostream& os, Person const& item)
{
os << item.name << " " << item.address << " ";
return os;
}
#endif
1、一个类可以包含0个或多个访问说明符,而且对于某个访问说明符能出现多少次也没严格限定。
2、每个访问说明符指定了接下来的成员的访问级别,其有效范围直到出现下一个访问说明符或者达到类的结尾处为止。
3、定义在public说明符之后的成员在整个程序内可被访问,public成员定义类的接口
4、定义在private说明符之后的成员可以被类的成员函数访问,但是不能被使用该类的代码访问,private部分封装了(即隐藏了)类的实现细节
使用class和struct定义类的唯一区别就是默认的访问权限。
参考:https://blog.csdn.net/shamozhizhoutx/article/details/82291127
封装是实现与接口的分离。它隐藏了类型的实现细节。(在C++中,封装是通过将实现放在一个类的私有部分来实现的) 封装有两个重要的优点:
1.确保用户代码不会无意间破坏封装对象的状态;
2.被封装的类的具体实现细节可以随时改变,而无须调整用户级别的代码。
接口应该被定义为公共的,数据不应该暴露在类之外。
class Person
{
public:
//内联自定义默认构造函数
Person(const string& s) :name(s) {} //可以单独输入一个name
Person(const string& s1, const string& s2) :name(s1), address(s2) {} //可以输入name和address
Person(istream& is) { read(is, *this); }
string get_name() const { return name; }
string get_address() const { return address; }
private:
string name; //人员姓名
string address; //住址
};
类可以允许其他类或者函数访问它的非公有成员,方法是令其他类或者函数成为它的友元。
优点: 外部函数可以方便地使用类的成员,而不需要显示地给它们加上类名; 可以方便地访问所有非公有成员; 有时,对类的用户更容易读懂。
缺点:减少封装和可维护性; 代码冗长,类内的声明,类外函数声明。
头文件
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include
#include
using namespace std;
struct Sales_data;
istream& read(std::istream& is, Sales_data& item);
ostream& print(std::ostream& os, const Sales_data& item);
Sales_data add(const Sales_data& lhs, const Sales_data& rhs); //先声明
class Sales_data
{
friend istream& read(std::istream& is, Sales_data& item);
friend 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 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); }
//一个isbn成员函数,用于返回对象的ISBN编号
string isbn() const { return bookNo; }
//一个combine成员函数,用于将一个Sales_data对象加到另一个对象上
Sales_data& combine(const Sales_data&);
//用于求平均价格
double avg_price() const; //平均价格
private:
string bookNo; //bookNo被初始化为空字符串,书籍名字
unsigned units_sold = 0; //初始化0,销售额
double revenue = 0.0; //初始化0,销售总价
};
Sales_data& Sales_data::combine(const Sales_data& rhs) //把后加入的书销售额和销售总价加到上一本书中(同名)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
double Sales_data::avg_price() const //平均价格
{
if (units_sold) {
return revenue / units_sold;
}
else {
return 0;
}
}
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 << " " << item.avg_price();
return os;
}
Sales_data add(const Sales_data& lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
#endif
主函数
#include
#include"Sales_data.h"
using namespace std;
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) << endl;
total = trans;
}
}
print(cout, total) << endl;
}
else {
cerr << "No data?!" << endl;
}
}

#ifndef PERSON_H
#define PERSON_H
#include
#include
using namespace std;
istream& read(istream& is, Person& item);
ostream& print(ostream& os, Person const& item);
class Person
{
friend istream& read(istream& is, Person& item);
friend ostream& print(ostream& os, Person const& item);
public:
//内联自定义默认构造函数
Person(const string& s) :name(s) {} //可以单独输入一个name
Person(const string& s1, const string& s2) :name(s1), address(s2) {} //可以输入name和address
Person(istream& is) { read(is, *this); }
string get_name() const { return name; }
string get_address() const { return address; }
private:
string name; //人员姓名
string address; //住址
};
istream& read(istream& is, Person& item)
{
is >> item.name >> item.address;
return is;
}
ostream& print(ostream& os, Person const& item)
{
os << item.name << " " << item.address << " ";
return os;
}
#endif
#ifndef SCREEN_H
#define SCREEB_H
#include
#include
#include
using namespace std;
class Screen
{
public:
typedef string::size_type pos; //用来定义类型的成员必须先定义后使用,这一点与普通成员有所区别,具体原因将在7.4.1节(第254页)解释。因此,类型成员通常出现在类开始的地方
private:
pos cursor = 0;
pos height = 0, width = 0;
string contents;
};
#endif // !SCREEN_H
#ifndef SCREEN_H
#define SCREEB_H
#include
#include
#include
using namespace std;
class Screen
{
public:
typedef string::size_type pos; //用来定义类型的成员必须先定义后使用,这一点与普通成员有所区别,具体原因将在7.4.1节(第254页)解释。因此,类型成员通常出现在类开始的地方
Screen() = default; //内联自定义默认构造函数
Screen(pos a, pos b) :height(a), width(b), contents(a* b, ' ') {} //构造函数接受宽和高的值,然后将contents初始化成给定数量的空白
Screen(pos a, pos b, const char& s) :height(a), width(b), contents(a* b, s) {} //构造函数接受宽和高的值以及一个字符
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;
string contents;
};
#endif // !SCREEN_H
能,Screen类中只有内置类型和string可以使用拷贝和赋值操作,见7.15。
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include
#include
using namespace std;
struct Sales_data;
istream& read(std::istream& is, Sales_data& item);
ostream& print(std::ostream& os, const Sales_data& item);
Sales_data add(const Sales_data& lhs, const Sales_data& rhs); //先声明
class Sales_data
{
friend istream& read(std::istream& is, Sales_data& item);
friend 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 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); }
//一个isbn成员函数,用于返回对象的ISBN编号
string isbn() const { return bookNo; }
//一个combine成员函数,用于将一个Sales_data对象加到另一个对象上
Sales_data& combine(const Sales_data&);
//用于求平均价格
private:
inline double avg_price() const; //平均价格
string bookNo; //bookNo被初始化为空字符串,书籍名字
unsigned units_sold = 0; //初始化0,销售额
double revenue = 0.0; //初始化0,销售总价
};
Sales_data& Sales_data::combine(const Sales_data& rhs) //把后加入的书销售额和销售总价加到上一本书中(同名)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
inline double Sales_data::avg_price() const //平均价格
{
if (units_sold) {
return revenue / units_sold;
}
else {
return 0;
}
}
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 << " " << item.avg_price();
return os;
}
Sales_data add(const Sales_data& lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
#endif
头文件
#ifndef SCREEN_H
#define SCREEB_H
#include
#include
#include
using namespace std;
class Screen
{
public:
typedef string::size_type pos; //用来定义类型的成员必须先定义后使用,这一点与普通成员有所区别,具体原因将在7.4.1节(第254页)解释。因此,类型成员通常出现在类开始的地方
Screen() = default; //内联自定义默认构造函数
Screen(pos a, pos b) :height(a), width(b), contents(a* b, ' ') {} //构造函数接受宽和高的值,然后将contents初始化成给定数量的空白
Screen(pos a, pos b, const char& s) :height(a), width(b), contents(a* b, s) {} //构造函数接受宽和高的值以及一个字符
char get()const { return contents[cursor]; } //读取光标处的字符,隐式内联
char get(pos r, pos c)const { return contents[r * width + c]; } //移动光标,输入行和列,行乘以宽到达指定行后加上列达到光标所在字符并返回
Screen& move(pos r, pos c);
Screen& set(char);
Screen& set(pos, pos, char);
Screen& display(ostream& os)
{
do_dispaly(os);
return *this;
}
const Screen& display(ostream& os)const
{
do_dispaly(os);
return *this;
}
private:
pos cursor = 0;
pos height = 0, width = 0;
string contents;
void do_dispaly(ostream& os)const { os << contents; }
};
inline Screen& Screen::move(pos r, pos c)
{
pos row = r * width; //计算行的位置
cursor = row + c; //在行内将光标移动到指定的列
return *this; //将this对象作为左值返回
}
inline Screen& Screen::set(char c)
{
contents[cursor] = c; //设置当前光标所在位置的新值
return *this; //将this对象作为左值返回
}
inline Screen& Screen::set(pos r, pos c, char ch)
{
contents[r * width + c] = ch; //设定给定位置的新值
return *this; //将this对象作为左值返回
}
#endif // !SCREEN_H
主函数
#include
#include"Screen.h"
using namespace std;
int main()
{
Screen myScreen(5, 5, 'X');
myScreen.move(4, 0).set('#').display(cout);
cout << "\n";
myScreen.display(cout);
cout << "\n";
}

返回类型是Screen&的输出:
XXXXXXXXXXXXXXXXXXXX#XXXX
XXXXXXXXXXXXXXXXXXXX#XXXX
返回类型是Screen的输出:
XXXXXXXXXXXXXXXXXXXX#XXXX
XXXXXXXXXXXXXXXXXXXXXXXXX
因为这样的话move、set和display返回的是Screen的临时副本,后续set和display操作并不会改变myScreen。
头文件
#ifndef SCREEN_H
#define SCREEB_H
#include
#include
#include
using namespace std;
class Screen
{
public:
typedef string::size_type pos; //用来定义类型的成员必须先定义后使用,这一点与普通成员有所区别,具体原因将在7.4.1节(第254页)解释。因此,类型成员通常出现在类开始的地方
Screen() = default; //内联自定义默认构造函数
Screen(pos a, pos b) :height(a), width(b), contents(a* b, ' ') {} //构造函数接受宽和高的值,然后将contents初始化成给定数量的空白
Screen(pos a, pos b, const char& s) :height(a), width(b), contents(a* b, s) {} //构造函数接受宽和高的值以及一个字符
char get()const { return contents[cursor]; } //读取光标处的字符,隐式内联
char get(pos r, pos c)const { return contents[r * width + c]; } //移动光标,输入行和列,行乘以宽到达指定行后加上列达到光标所在字符并返回
Screen move(pos r, pos c);
Screen set(char);
Screen set(pos, pos, char);
Screen& display(ostream& os)
{
do_dispaly(os);
return *this;
}
const Screen& display(ostream& os)const
{
do_dispaly(os);
return *this;
}
private:
pos cursor = 0;
pos height = 0, width = 0;
string contents;
void do_dispaly(ostream& os)const { os << contents; }
};
inline Screen Screen::move(pos r, pos c)
{
pos row = r * width; //计算行的位置
cursor = row + c; //在行内将光标移动到指定的列
return *this; //将this对象作为左值返回
}
inline Screen Screen::set(char c)
{
contents[cursor] = c; //设置当前光标所在位置的新值
return *this; //将this对象作为左值返回
}
inline Screen Screen::set(pos r, pos c, char ch)
{
contents[r * width + c] = ch; //设定给定位置的新值
return *this; //将this对象作为左值返回
}
#endif // !SCREEN_H
主函数
#include
#include"Screen.h"
using namespace std;
int main()
{
Screen myScreen(5, 5, 'X');
myScreen.move(4, 0).set('#').display(cout);
cout << "\n";
myScreen.display(cout);
cout << "\n";
}

优点: 更明确,减少误读的可能性; 可以使用名称与成员名相同的形参。
void setAddr(const std::string &addr) { this->addr = addr; }
缺点: 冗余代码增加。
std::string getAddr() const { return this->addr; } // unnecessary
#include
using namespace std;
class X
{
Y* y = nullptr;
};
class Y
{
X A;
};
#ifndef SCREEN_H
#define SCREEB_H
#include
#include
#include
using namespace std;
class Window_mgr //Screen的构造函数接受两个尺寸参数和一个字符值,创建了一个给定大小的空白屏幕对象(24*80)
{
public:
using ScreenIndex = vector<Screen>::size_type;
void clear(ScreenIndex);
private:
vector<Screen> screens{ Screen(24,80,' ') }; //定义一个高24,宽80的空字符Screen
};
class Screen
{
friend void Window_mgr::clear(ScreenIndex i);
public:
typedef string::size_type pos; //用来定义类型的成员必须先定义后使用,这一点与普通成员有所区别,具体原因将在7.4.1节(第254页)解释。因此,类型成员通常出现在类开始的地方
Screen() = default; //内联自定义默认构造函数
Screen(pos a, pos b) :height(a), width(b), contents(a* b, ' ') {} //构造函数接受宽和高的值,然后将contents初始化成给定数量的空白
Screen(pos a, pos b, const char& s) :height(a), width(b), contents(a* b, s) {} //构造函数接受宽和高的值以及一个字符
char get()const { return contents[cursor]; } //读取光标处的字符,隐式内联
char get(pos r, pos c)const { return contents[r * width + c]; } //移动光标,输入行和列,行乘以宽到达指定行后加上列达到光标所在字符并返回
Screen& move(pos r, pos c);
Screen& set(char);
Screen& set(pos, pos, char);
Screen& display(ostream& os)
{
do_dispaly(os);
return *this;
}
const Screen& display(ostream& os)const
{
do_dispaly(os);
return *this;
}
private:
pos cursor = 0;
pos height = 0, width = 0;
string contents;
void do_dispaly(ostream& os)const { os << contents; }
};
inline Screen& Screen::move(pos r, pos c)
{
pos row = r * width; //计算行的位置
cursor = row + c; //在行内将光标移动到指定的列
return *this; //将this对象作为左值返回
}
inline Screen& Screen::set(char c)
{
contents[cursor] = c; //设置当前光标所在位置的新值
return *this; //将this对象作为左值返回
}
inline Screen& Screen::set(pos r, pos c, char ch)
{
contents[r * width + c] = ch; //设定给定位置的新值
return *this; //将this对象作为左值返回
}
void Window_mgr::clear(ScreenIndex i)
{
Screen& s = screens[i];
s.contents = string(s.height * s.width, ' ');
}
#endif // !SCREEN_H
#ifndef SCREEN_H
#define SCREEB_H
#include
#include
#include
using namespace std;
class Window_mgr //Screen的构造函数接受两个尺寸参数和一个字符值,创建了一个给定大小的空白屏幕对象(24*80)
{
public:
using ScreenIndex = vector<Screen>::size_type;
void clear(ScreenIndex);
private:
vector<Screen> screens{ Screen(24,80,' ') }; //定义一个高24,宽80的空字符Screen
};
class Screen
{
friend void Window_mgr::clear(ScreenIndex i);
public:
typedef string::size_type pos; //用来定义类型的成员必须先定义后使用,这一点与普通成员有所区别,具体原因将在7.4.1节(第254页)解释。因此,类型成员通常出现在类开始的地方
Screen() = default; //内联自定义默认构造函数
Screen(pos a, pos b) :height(a), width(b), contents(a* b, ' ') {} //构造函数接受宽和高的值,然后将contents初始化成给定数量的空白
Screen(pos a, pos b, const char& s) :height(a), width(b), contents(a* b, s) {} //构造函数接受宽和高的值以及一个字符
char get()const { return contents[cursor]; } //读取光标处的字符,隐式内联
char get(pos r, pos c)const { return contents[r * width + c]; } //移动光标,输入行和列,行乘以宽到达指定行后加上列达到光标所在字符并返回
Screen& move(pos r, pos c);
Screen& set(char);
Screen& set(pos, pos, char);
Screen& display(ostream& os)
{
do_dispaly(os);
return *this;
}
const Screen& display(ostream& os)const
{
do_dispaly(os);
return *this;
}
pos size()const;
private:
pos cursor = 0;
pos height = 0, width = 0;
string contents;
void do_dispaly(ostream& os)const { os << contents; }
};
inline Screen& Screen::move(pos r, pos c)
{
pos row = r * width; //计算行的位置
cursor = row + c; //在行内将光标移动到指定的列
return *this; //将this对象作为左值返回
}
inline Screen& Screen::set(char c)
{
contents[cursor] = c; //设置当前光标所在位置的新值
return *this; //将this对象作为左值返回
}
inline Screen& Screen::set(pos r, pos c, char ch)
{
contents[r * width + c] = ch; //设定给定位置的新值
return *this; //将this对象作为左值返回
}
void Window_mgr::clear(ScreenIndex i)
{
Screen& s = screens[i];
s.contents = string(s.height * s.width, ' ');
}
Screen::pos Screen::size() const
{
return height * width;
}
#endif // !SCREEN_H
dummy_fcn(pos height)中的pos未声明,将会报错。
修改前
#ifndef EXERCISE_H
#define EXERCISE_H
#include
#include
using namespace std;
typedef string Type;
Type initVal();
class Exercise
{
public:
typedef double Type;
Type setVal(Type);
Type initVal();
private:
int val;
};
Type Exercise::setVal(Type parm) {
val = parm + initVal();
return val;
}
#endif // !EXERCISE_H
修改后
#ifndef EXERCISE_H
#define EXERCISE_H
#include
#include
using namespace std;
typedef string Type;
Type initVal();
class Exercise
{
public:
typedef double Type;
Type setVal(Type);
Type initVal();
private:
int val;
};
Exercise::Type Exercise::setVal(Type parm) {
val = parm + initVal();
return val;
}
#endif // !EXERCISE_H
在type前加一个类名
Exercise::Type Exercise::setVal(Type parm) {
val = parm + initVal();
return val;
}
修改前
struct X {
X(int i, int j) :base(i), rem(base % j) {}
int rem, base;
};
修改后,成员的初始化顺序与它们在类定义中的出现顺序一致,所以会先初始化rem再初始化base,初始化rem时会用到base,故程序出错。可以改变定义的顺序
struct X {
X(int i, int j) :base(i), rem(base % j) {}
int base, rem;
};
参考:https://blog.csdn.net/shamozhizhoutx/article/details/82291127
#include
#include"Sales_data.h"
using namespace std;
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(std::string s = ""); bookNo = "", cnt = 0, revenue = 0.0
Sales_data last("9-999-99999-9"); // use Sales_data(std::string s = ""); bookNo = "9-999-99999-9", cnt = 0, revenue = 0.0
}
Sales_data(istream& is) { read(is, *this); }
非法。因为这样的话,重载构造函数Sale_data()将不明确。
(a) Book
(b) Data
(c) Employee
(d) Vehicle
(e) Object
(f) Tree
#ifndef OBJECT_H
#define OBJECT_H
#include
#include
using namespace std;
class Object
{
using pos = size_t;
public:
//内联自定义默认构造函数
Object() = default;
Object(const string& s, pos lt, pos wt, pos ht) :name(s), longth(lt), width(wt), height(ht), volume(lt* wt* ht) {}
Object(const string& s) :name(s) {};
Object(pos lt, pos wt, pos ht) :volume(lt* wt* ht) { }
private:
pos height, width, longth; //物体高、宽、长
string name; //物体名字
pos volume; //物体体积
};
#endif // !OBJECT_H
第一次修改
主函数
#include
#include"Sales_data.h"
using namespace std;
int main()
{
Sales_data book1;
}
头文件
这里主要是将Sales_data()=default;修改了成Sales_data() :Sales_data(" ", 0, 0) { cout << "go" << endl; }
在类外部声明Sales_data data;时默认bookNo为空,units_sold = 0,revenue = 0.0并返回一条语句go
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include
#include
using namespace std;
struct Sales_data;
istream& read(std::istream& is, Sales_data& item);
ostream& print(std::ostream& os, const Sales_data& item);
Sales_data add(const Sales_data& lhs, const Sales_data& rhs); //先声明
class Sales_data
{
friend istream& read(std::istream& is, Sales_data& item);
friend ostream& print(std::ostream& os, const Sales_data& item);
friend Sales_data add(const Sales_data& lhs, const Sales_data& rhs);
public:
//内联自定义默认构造函数,这里还加上了委托构造函数
Sales_data() :Sales_data(" ", 0, 0) { cout << "go" << endl; } //在类外部声明Sales_data data;时默认bookNo为空,units_sold = 0,revenue = 0.0并返回一条语句go
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); }
//一个isbn成员函数,用于返回对象的ISBN编号
string isbn() const { return bookNo; }
//一个combine成员函数,用于将一个Sales_data对象加到另一个对象上
Sales_data& combine(const Sales_data&);
//用于求平均价格
private:
inline double avg_price() const; //平均价格
string bookNo; //bookNo被初始化为空字符串,书籍名字
unsigned units_sold = 0; //初始化0,销售额
double revenue = 0.0; //初始化0,销售总价
};
Sales_data& Sales_data::combine(const Sales_data& rhs) //把后加入的书销售额和销售总价加到上一本书中(同名)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
inline double Sales_data::avg_price() const //平均价格
{
if (units_sold) {
return revenue / units_sold;
}
else {
return 0;
}
}
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 << " " << item.avg_price();
return os;
}
Sales_data add(const Sales_data& lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
#endif
执行主函数不出所料返回了一个go

第二次修改
主函数
#include
#include"Sales_data.h"
using namespace std;
int main()
{
string name = "1-0-1";
Sales_data book1;
Sales_data book2(name);
Sales_data book3("1-0-2");
}
头文件
这里主要是将Sales_data(const string& s) :bookNo(s) {}修改了成Sales_data(const string& s) :Sales_data(s, 0, 0) { cout << "go" << endl; }
在类外部声明Sales_data data(s);时默认bookNo为s,units_sold = 0,revenue = 0.0并返回一条语句go
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include
#include
using namespace std;
struct Sales_data;
istream& read(std::istream& is, Sales_data& item);
ostream& print(std::ostream& os, const Sales_data& item);
Sales_data add(const Sales_data& lhs, const Sales_data& rhs); //先声明
class Sales_data
{
friend istream& read(std::istream& is, Sales_data& item);
friend ostream& print(std::ostream& os, const Sales_data& item);
friend Sales_data add(const Sales_data& lhs, const Sales_data& rhs);
public:
//内联自定义默认构造函数,这里还加上了委托构造函数
Sales_data() :Sales_data(" ", 0, 0) { cout << "go" << endl; } //在类外部声明Sales_data data;时默认bookNo为空,units_sold = 0,revenue = 0.0并返回一条语句go
Sales_data(const string& s) :Sales_data(s, 0, 0) { cout << "go" << endl; } //在类外部声明Sales_data data(s);时默认bookNo为s,units_sold = 0,revenue = 0.0并返回一条语句go
//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); }
//一个isbn成员函数,用于返回对象的ISBN编号
string isbn() const { return bookNo; }
//一个combine成员函数,用于将一个Sales_data对象加到另一个对象上
Sales_data& combine(const Sales_data&);
//用于求平均价格
private:
inline double avg_price() const; //平均价格
string bookNo; //bookNo被初始化为空字符串,书籍名字
unsigned units_sold = 0; //初始化0,销售额
double revenue = 0.0; //初始化0,销售总价
};
Sales_data& Sales_data::combine(const Sales_data& rhs) //把后加入的书销售额和销售总价加到上一本书中(同名)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
inline double Sales_data::avg_price() const //平均价格
{
if (units_sold) {
return revenue / units_sold;
}
else {
return 0;
}
}
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 << " " << item.avg_price();
return os;
}
Sales_data add(const Sales_data& lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
#endif

修改后
主函数
#include
#include"Sales_data.h"
using namespace std;
int main()
{
string name = "1-0-1";
Sales_data book1;
Sales_data book2(name);
Sales_data book3("1-0-2");
Sales_data book4(cin);
}
头文件
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include
#include
using namespace std;
struct Sales_data;
istream& read(std::istream& is, Sales_data& item);
ostream& print(std::ostream& os, const Sales_data& item);
Sales_data add(const Sales_data& lhs, const Sales_data& rhs); //先声明
class Sales_data
{
friend istream& read(std::istream& is, Sales_data& item);
friend 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 string& s, unsigned n, double p) :bookNo(s), units_sold(n), revenue(p* n) {} //非委托构造函数使用对应的实参初始化成员
Sales_data() :Sales_data(" ", 0, 0) { cout << "go" << endl; } //在类外部声明Sales_data data;时默认bookNo为空,units_sold = 0,revenue = 0.0并返回一条语句go
Sales_data(const string& s) :Sales_data(s, 0, 0) { cout << "go" << endl; } //在类外部声明Sales_data data(s);时默认bookNo为s,units_sold = 0,revenue = 0.0并返回一条语句go
Sales_data(istream& is) :Sales_data() { read(is, *this); cout << "go" << endl; } //委托给了默认构造参数变成了Sales_data(" ", 0, 0),之后再read
//Sales_data()=default;
//Sales_data(const string& s) :bookNo(s) {}
//Sales_data(istream& is) { read(is, *this); }
//一个isbn成员函数,用于返回对象的ISBN编号
string isbn() const { return bookNo; }
//一个combine成员函数,用于将一个Sales_data对象加到另一个对象上
Sales_data& combine(const Sales_data&);
//用于求平均价格
private:
inline double avg_price() const; //平均价格
string bookNo; //bookNo被初始化为空字符串,书籍名字
unsigned units_sold = 0; //初始化0,销售额
double revenue = 0.0; //初始化0,销售总价
};
Sales_data& Sales_data::combine(const Sales_data& rhs) //把后加入的书销售额和销售总价加到上一本书中(同名)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
inline double Sales_data::avg_price() const //平均价格
{
if (units_sold) {
return revenue / units_sold;
}
else {
return 0;
}
}
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 << " " << item.avg_price();
return os;
}
Sales_data add(const Sales_data& lhs, const Sales_data& rhs)
{
Sales_data sum = lhs;
sum.combine(rhs);
return sum;
}
#endif

#ifndef OBJECT_H
#define OBJECT_H
#include
#include
using namespace std;
class Object
{
using pos = size_t;
public:
//内联自定义默认构造函数
Object(const string& s, pos lt, pos wt, pos ht) :name(s), longth(lt), width(wt), height(ht), volume(lt* wt* ht) {}
//Object() = default;
//Object(const string& s) :name(s) {};
//Object(pos lt, pos wt, pos ht) :volume(lt* wt* ht) { }
//委托构造函数
Object() :Object(" ", 0, 0, 0) {}
Object(const string& s) :Object(s, 0, 0, 0) {}
Object(pos lt, pos wt, pos ht) :Object(" ", lt, wt, ht) {}
private:
pos height, width, longth; //物体高、宽、长
string name; //物体名字
pos volume; //物体体积
};
#endif // !OBJECT_H
#ifndef NODEFAULT_H
#define NODEFAULT_T
#include
#include
using namespace std;
class NoDefault
{
public:
NoDefault(int j) :i(j) {}
private:
int i;
};
class C
{
public:
C() :a(0) {}
private:
NoDefault a;
};
#endif // !NODEFAULT_H
非法,因为NoDefault没有默认构造函数
#include
#include"NoDefault.h"
int main()
{
vector<NoDefault> vec(10);
}
合法,因为C有默认构造函数。
#include
#include"NoDefault.h"
int main()
{
vector<C> vec(10);
}
(a) 一个类必须至少提供一个构造函数。
不正确,没有构造函数时,有时可以生成默认构造函数;
(b)默认构造函数是参数列表为空的构造函数。
不正确,默认构造函数是没有构造函数的情况下,由编译器生成的构造函数;
©如果对于类来说不存在有意义的默认值,则类不应该提供默认构造函数。
不正确,默认构造函数在一些情况下非常重要;
(d)如果类没有定义默认构造函数,则编译器将为其生成一个并把每个数据成员初始化成相应类型的默认值。
不正确,当类没有显式地定义构造函数时,编译器才会隐式地定义默认构造函数。
优点:防止隐式转换的产生;可以只用作初始化。
缺点:只有个单个参数的构造函数才有意义。
都不会有问题。
#include
#include"Sales_data.h"
using namespace std;
int main()
{
string null_isbn("9-999-99999-9");
Sales_data item1(null_isbn);
Sales_data item2("9-999-99999-9");
}
(a)正确;
(b)不正确,combine的参数是非常量的引用,所以我们不能将临时参数传递给它,改成Sales_data&combine(const Sales_data&); 后正确;
(c)不正确,后面的const不对,this需要可改变的。
#ifndef PERSON_H
#define PERSON_H
#include
#include
using namespace std;
istream& read(istream& is, Person& item);
ostream& print(ostream& os, Person const& item);
class Person
{
friend istream& read(istream& is, Person& item);
friend ostream& print(ostream& os, Person const& item);
public:
//内联自定义默认构造函数
Person(const string& s) :name(s) {} //可以单独输入一个name
Person(const string& s1, const string& s2) :name(s1), address(s2) {} //可以输入name和address
explicit Person(istream& is) { read(is, *this); }
string get_name() const { return name; }
string get_address() const { return address; }
private:
string name; //人员姓名
string address; //住址
};
istream& read(istream& is, Person& item)
{
is >> item.name >> item.address;
return is;
}
ostream& print(ostream& os, Person const& item)
{
os << item.name << " " << item.address << " ";
return os;
}
#endif
以下函数:
int getSize(const std::vector<int>&);
如果vector的构造函数没有explicit,
getSize(34);
我们就会不明白上述函数的意思。
stirng则不同,下述函数我们就很清楚。
void setYourName(std::string); // declaration.
setYourName("pezy"); // just fine.
该初始化使用花括号括起来的成员初始值列表来初始化聚合类的数据成员。所以我们需要定义聚合类:
#include
#include
using namespace std;
struct Sales_data {
string bookNo;
unsigned units_sold;
double revenue;
};
int main()
{
Sales_data item = { "978-0590353403",25,15.99 };
}
#ifndef DEBUG_H
#define DEBUG_H
#include
#include
#include
using namespace std;
class Debug;
class Debug
{
public:
constexpr Debug(bool h, bool i, bool o) :hw(h), io(i), other(o) {}
constexpr Debug(bool h = true) : hw(h).io(h), other(h) {};
constexpr bool any() { return hw || io || other; }
void set_io(bool i) { io = i; }
void set_hw(bool h) { hw = h; }
void set_other(bool o) { other = o; }
private:
bool hw; //硬件错误,而非io错误
bool io; //IO错误
bool other; //其他错误
};
#endif // !DEBUG_H
在C++11中,constexpr函数时隐式的const,将不能更改数据成员;C++14中没有这个特点。
不是,std::string不是字面值类型。
类的静态成员与类本身直接相关,而不是与类的各个对象保持关联。
每个对象不需要存储公共数据,如果数据被改变,则每个对象都可以使用新值。
静态数据成员可以是不完全类型;
可以使用静态成员作为默认实参。
#ifndef ACCOUNT_H
#define ACCOUNT_H
#include
#include
#include
using namespace std;
class Account;
class Account
{
public:
void calculate() { amount += amount * interestRate; }
static double rate() { return interestRate; }
static void rate(double);
private:
string owner;
double amount;
static double interestRate;
static double initRate();
};
void Account::rate(double newRate)
{
interestRate = newRate;
}
#endif // !ACCOUNT_H
修改前
#ifndef EXAMPLE_H
#define EXAMPLE_H
#include
#include
#include
using namespace std;
class Example;
class Example {
public:
static double rate = 6.5;
static const int vecSize = 20;
static vector<double> vec(vecSize);
};
#endif // !EXAMPLE_H
#include
#include
using namespace std;
#include "example.h"
int main()
{
double Example::rate;
vector<double> Example::vec;
}
修改后
#ifndef EXAMPLE_H
#define EXAMPLE_H
#include
#include
#include
using namespace std;
class Example;
class Example {
public:
static double rate;
static const int vecSize = 20;
static vector<double> vec;
};
#endif // !EXAMPLE_H
#include
#include
using namespace std;
#include "example.h"
double Example::rate = 6.5;
vector<double> Example::vec(vecSize);