通过代码来学的这一章,所以总结也跟着代码来。
//
// Created by sequin on 2/15/17.
//
#include <iostream>
using namespace std;
struct Sales_data{
//常量成员函数
string isbn() const {return bookNo;} //bookNo其实就是定义在Sales_data 中的数据成员
/*
const在这里的作用是修改 隐式this指针的类型。默认情况下this的类型是指向 类 类型 非 常量版本的 常量指针。
例如:Sales_data *const this。
这意味着我们不能把this绑定到一个常量对象上(常量指针一旦初始化,其值不能改变),那么在isbn()函数内部不会改变this所指向的对象则可以将this设置为指向常量的指针,
例如: const Sales_data *const this
但是问题是,this是隐式的,不会出现在参数列表中。这种情况下把const关键字放在成员函数的参数列表后,就可以表示this是一个指向常量的指针。
类似于
String Sales_data isbn(const Sales_data *const this){
return this->isbn;
}
*/
Sales_data& combine(const Sales_data&);
double avg_price() const;
//自动生成默认构造函数
string bookNo; //虽然bookNo定义在isbn之后,isbn依然可以使用bookNo。这是因为编译器首先编译成员的声明,然后才编译成员函数体,所以可以不用在意成员出现的次序。
unsigned units_sold = 0;
double revenue = 0.0;
};
/*
*合成的默认构造函数只适合非常简单的类,某些类不能依赖 默认构造函数 必须定义它自己的默认构造函数。
*首先编译器只在没有编写构造函数的情况下才会生成一个默认的构造函数,如果已经定义了其他的构造函数,编译器不会再生成默认的构造函数了。
*其次,默认构造函数可能会执行错误的操作,块内置类型的对象被默认初始化,它的值则是未定义。
*最后一个原因是某些情况下编译器不不能为某些类合成默认的构造函数。例如,如果类中包含了 其它类 类型成员并且这个成员的类型没有默认构造函数则无法初始化该成员。
*/
//改写Sales_data,定义其构造函数
/*
* struct 和class的区别是默认访问权限不同
* struct默认成员是public的,而class默认成员是private的
*/
class Sales_data{
//友元只能声明在类定义的内部
friend Sales_data add(const Sales_data&, const Sales_data&);
friend istream &read(istream&, Sales_data&);
friend ostream &print(ostream&, const Sales_data&);
public:
Sales_data() = default; //该构造函数不接受任何实参,是一个默认构造函数,=default 在类内,则默认 构造函数是内联的。
//构造初始函数值列表:以及{}之间的代码
Sales_data(const string &s) : bookNo(s){} //花括号定义了函数体,此时为空,
/*
当某个数据成员被构造函数初始化值表忽略时,它将以合成默认构造函数相同的方式隐式初始化。
相当于
*/
Sales_data(const string &s):bookNo(s), units_sold(0), revenue(0){}
Sales_data(const string &s, unsigned n, double p) : bookNo(s), units_sold(n), revenue(p*n){}
Sales_data(istream &);
string isbn() const {return bookNo;}
Sales_data& combine(const Sales_data&);
private:
double avg_price() const;
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
/*
* 因为权限限定,read print add这时是无法正常编译的,虽然他们是类的接口的一部分,但不是类的成员
* 只有让想要访问 类的非公有成员 的其他函数或是其他类 成为此类的友元, 需要增加一条以friend关键字开头的函数声明语句, 如上面。
*/
};
/*
* 访问说明符加强类的封装性
* public在整个程序内都可以访问,一般定义类的接口
* private可以被类内的成员函数访问,但是不能被使用该类的代码访问,一般封装类的实现细节
*/
//Sales_data接口的非成员组部分声明
Sales_data add(const Sales_data&, const Sales_data&);
ostream &print(ostream&, const Sales_data&);
istream &read(istream&, Sales_data&);
//当在类外定义成员函数时,成员函数的定义必须与它的声明匹配。即,返回参数、参数列表和函数名都得与类内的声明保持一致,成员名字必须包含它所属的类名。
double Sales_data::avg_price() const {
//隐式使用了Sales_data的成员
if(units_sold){
return revenue/units_sold;
}
else
return 0;
}
//类似的复合运算的+=, 调用该函数的对象代表的是赋值运算符左侧的运算对象, 右侧运算对象则通过显式的实参被传入函数。函数返回引用类型。
Sales_data& Sales_data::combine(const Sales_data &rhs) {
units_sold += rhs.units_sold; //把rhs的成员加到this对象的成员上
revenue += rhs.revenue;
return *this;//返回调用该函数的对象, 解引用this指针获得执行该函数的对象。
}
/*
read和print分别接受一个各自的IO类型的引用作为其参数,这是因为IO类属于不能被拷贝的类型,只能通过引用来传递它们。
因为读取和写入的操作会改变流的内容,所以两个函数接受的都是普通引用,而不是对常量的引用。
*/
//read 函数从给定流中将数据读到给定的对象里
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;
}
/*
构造函数没有返回类型,在类外定义构造函数时,必须指明该构造函数是哪个类的成员,并且构造函数和类名相同。
Sales_data::Sales_data(istream &is){ //构造函数初始值列表为空,但有函数体。
//read的作用是从is中读取一条交易记录然后存入this对象中
read(is, *this); //使用this把对象当成一个整体访问,使用*this将this对象作为实参传递给read函数
}
*/
//将给定对象的内容打印到给定的流中
ostream &print(ostream &os, const Sales_data &item){
os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price();
return os;
}
int main(){,
Sales_data total;
if(read(cin, total)){
Sales_data trans;
while(read(cin, trans)){
if(total.isbn() == trans.isbn()){
total.combine(trans);//更新变量total当前的值
}
else{
print(cout, total) << endl;
total = trans;
}
}
print(cout, total) << endl;
}
else{
cerr << "No data?!" << endl;
}
}
//
// Created by sequin on 2/15/17.
//
#include <iostream>
#include <vector>
using namespace std;
class Screen{
public:
typedef string::size_type pos; //类可以自定义某种类型在类中的别名, 且必须先定义后使用
//等价于 using pos = string::size_type;
Screen() = default;
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 ht, pos wd) const; //显式内联,定义在类内部的函数是自动inline的,因此,get函数默认是line函数
/*
* 重载成员函数。Screen定义了两个版本的get,编译器根据实际参数的 数量 来决定运行哪个
* Screen myscreen;
* char ch = myscreen.get(); 调用Screen::get()
* ch = myscreen.get(0, 0); 调用Screen::get(pos, pos)
*/
Screen &move(pos r, pos c); //能在之后被设置为内联
void some_member() const;
/*
* 如果希望可以修改类的某个数据成员,可一将它变成可变数据成员
* 一个可变数据成员永远不会const,即使它是const对象的成员
*/
public:
//设置光标所在位置的字符或者其他人一位置给定位置的字符
Screen &set(char);
Screen &set(pos, pos, char);
public:
//根据对象是否是const重载了display函数
Screen &display(ostream &os){
do_display(os);
return *this;
}
const Screen &display(ostream &os) const{
do_display(os);
return *this;
}
/*
* 当display调用do_display时,它的this指针隐式的传递给do_display。
* 当display的非 常量版本调用do_display时,它的this指针将隐式的从指向非常量的指针转化成指向常量的指针
*/
private:
//显示Screen的内容
void do_display(ostream &os) const{os << contents;}
private:
pos cursor = 0;
pos height = 0, width = 0;
string contents;
mutable size_t access_ctr; //在变量的声明中加入mutable关键字。即使在一个const成员变量中也能够被修改
};
//尽管some_member是一个const成员函数,但是该成员也是一个可变成员函数,因此任何成员都能改变它的值
void Screen::some_member() const {
++access_ctr; //保存一个计数值,用于记录成员函数被调用的次数
}
//在类外部修饰函数
inline Screen &Screen::move(pos r, pos c){
pos row = r * width; //计算行的位置
cursor = row + c; //在行内将光标移动到指定的列
return *this; //返回对象
}
//在类的内部声明为内联函数
char Screen::get(pos r, pos c) const{
pos row = r * width; //计算行的位置
return contents[row + c]; //返回给定列的字符
}
inline Screen &Screen::set(char c){
contents[cursor] = c; //设置当前光标所在位置的新值
return *this;
}
inline Screen &Screen::set(pos r, pos col, char ch){
contents[r * width + col] = ch; //设置给定位置的新值
return *this;
}
class Window_mgr{
private:
vector<Screen> screens{Screen(24, 80, ' ')}; //当初始化类类型成员时,需要为构造函数传递一个符合成员类型的实参。
};
int main(){
Screen myScreen(5, 5, 'X');
myScreen.move(4, 0).set('#').display(cout);
cout << endl;
myScreen.display(cout);
cout << endl;
}