#ifndef SALES_DATA_H
#define SALES_DATA_H
#include <iostream>
#include <string>
using std::cout;
using std::cin;
using std::endl;
using std::cerr;
using std::istream;
using std::ostream;
using std::string;
// 类的基本思想:数据抽象(data abstraction)和封装(encapsulation)
// 数据抽象是依赖于接口(interface)和实现(implementation)分离的编程技术
// 封装实现了类的接口和实现的分离
// const 成员函数:修改隐式 this 指针的类型,const 关键字放在形参列表后面
//常量对象,常量对象的引用或指针都只能调用常量成员函数
//前向声明(forward declaration),向程序引入名字 Sales_data,并指明是一个 class 类型
//从声明开始直到定义之前,Sales_data 是一个未完全类型(incomplete type)
//可以定义指向这种类型的指针或引用,也可以声明(但不能定义)以这种类型为参数或返回类型的函数
class Sales_data;
//class 和 struct 的唯一区别就是默认访问权限的不同
class Sales_data
{
//友元函数,允许其他类或函数,访问该类的非公有成员
//友元的声明只是访问权限的设定,并不能代替函数声明
//定义其他类的友元关系 friend class classname
//定义其他类成员函数的友元关系 friend classname::function, 必须先声明该函数
friend ostream &print(ostream &os, const Sales_data &tmp);
friend istream &read(istream &is, Sales_data &tmp);
//访问说明符,public 之后的成员在整个程序内可以被访问,多用于定义类的接口
public:
// 构造函数(constructor),只要类的对象被创建,就会执行构造函数
// 没有返回函数,有一个形参列表和函数体,可以被重载,但不能声明成 const
// 默认构造函数,无需任何实参,如果没有显式定义构造函数,编译器创建合成默认构造函数
//初始化数据成员规则:
//如果存在类内的初始值,用它来初始化成员
//否则默认初始化该成员
//默认初始化成员会出现未定义的情况,此时默认构造函数可能执行错误的操作
//如果含有类类型的成员而且该成员没有默认构造函数,无法执行该成员的初始化
// 通过 s,n,m 构造对象
Sales_data(string &s, double n, unsigned m):bookNo(s), revenue(n * m), units_sold(m)
{}
// = default 要求编译器生成构造函数
Sales_data() = default;
// 通过 s 字串构造对象
//委托构造函数(delegating constractor)
Sales_data(string &s):Sales_data(s, 0.0, 0){}
// 通过 is 标准输入流的数据构造对象
//通过关键字 explicit 抑制隐式类类型转换,只能在类内的构造声明出现,不能出现在类外
//定义 explicit 后,只能使用直接初始化,不能用拷贝初始化
explicit Sales_data(istream &is);
//const 成员函数,获得私有成员变量
string isbn() const { return bookNo; }
//普通成员函数,将 tmp 的对象数据,合并到对应成员数据中
Sales_data &combin(const Sales_data &tmp);
//private 访问说明符之后的成员可以被类的成员函数访问,但不能被使用该类的代码访问,隐藏类的实现细节
private:
// 定义在类内部的函数是隐式的 inline 函数
double avg_price() const
{
return units_sold ? (revenue / units_sold) : 0;
}
//成员的初始化顺序和他们在类中出现的顺序一致
double revenue = 0.0;
unsigned units_sold = 0;
string bookNo;
//静态成员,存在于任何对象之外,对象中不包含任何与静态成员相关的数据
//静态成员函数,不包含 this 指针,也不能定义为 const
//通常使用 域操作符调用 ,也可以同过. ->使用对象调用
//不通过构造函数初始化,应在类的外部定义并初始化
//可以是不完全类型,可用静态成员变量作为默认实参
static double sta_test;
public:
//可变数据成员,(mutable data member)永远不会是 const
mutable size_t count = 0;
};
//非成员函数声明
//输出 tmp 对象的信息
ostream &print(ostream &os,const Sales_data &tmp);
//从 is 流中读入数据,用于初始化 tmp 对象
istream &read(istream &is, Sales_data &tmp);
//接受2个对象,返回一个新的对象,表示他们的和
Sales_data add(const Sales_data &lTmp, const Sales_data &rTmp);
#endif //SALES_DATA_H
#include "Sales_data.h"
Sales_data::Sales_data(istream &is)
{
read(cin, *this);
}
Sales_data &Sales_data::combin(const Sales_data &tmp)
{
units_sold += tmp.units_sold;
revenue += tmp.revenue;
return *this;
}
ostream &print(ostream &os,const Sales_data &tmp)
{
os << tmp.bookNo << "(" << tmp.units_sold << ") : "
<< tmp.revenue << " " << tmp.avg_price();
}
istream &read(istream &is, Sales_data &tmp)
{
cout << "pls input : bn num price :" << endl;
is >> tmp.bookNo >> tmp.units_sold >>tmp.revenue;
}
Sales_data add(const Sales_data &lTmp, const Sales_data &rTmp)
{
Sales_data sum = lTmp;
sum.combin(rTmp);
return sum;
}
#include "Sales_data.h"
int main()
{
//类名直接作为 类型名使用,即使2个类的成员完全一致,也是不同的类型
//定义对象前,必须完全定义类,因此类不能包含自身类类型的成员
//类名出现后,就被认为是声明过了,因此可以包含指向自身类型的引用或指针
Sales_data total; //等价于 class Sales_data total;
const Sales_data mutableTest;
Sales_data test1_total;
//可变成员变量
cout << mutableTest.count << endl;
++mutableTest.count;
cout << mutableTest.count << endl;
//隐式类类型转换,仅用于只有一个参数的构造函数,多个参数的不会发生
string test_book = "99-99-99";
//利用test_book创建了临时对象,该转换的步骤只能有一次
Sales_data test2_total = test_book;
//强制类型转换,打破 explicit 限制
test2_totalcombin(static_cast<Sales_data>(cin));
//test_total.combin("99-99-99"); 错误,有2步转换,先由 const char [] 转为 string,再有string 转换为类类型
test1_total.combin(test_book);
if( read( cin, total))
{
Sales_data trans;
while( read( cin, trans))
{
if( trans.isbn() == total.isbn())
total.combin(trans);
else
{
print(cout, total) << endl;
total = trans;
}
}
print(cout, total);
}
else
{
cerr << "NO DATA!" << endl;
return 0;
}
return 1;
}
练习
<pre name="code" class="cpp">#ifndef SCREEN_DATA_H
#define SCREEN_DATA_H
#include<string>
#include<iostream>
using std::string;
using std::istream;
using std::ostream;
using std::cout;
using std::cin;
using std::endl;
class Screen
{
friend class Mgr_screen;
// friend void Mgr_screen::clean(Mgr_screen::screensIndex);//单独声明一个友元...这个好难
public:
typedef string::size_type pos;
//如果一个构造函数为所有形参都提供了默认实参,实际上它也是一个默认构造函数
Screen() = default;
Screen(pos a, pos b):
x(a),y(b),contents(a * b, 'O'){}
Screen(pos a, pos b, char c):
x(a),y(b), contents(a * b, c){}
char get() const
{
return contents[curson];
}
char get(pos w, pos h) const
{
return contents[h * x + w];
}
Screen &move(pos, pos);
Screen &set(char);
Screen &set(pos, pos, char);
Screen &display(ostream &os)
{
do_display(os);
return *this;
}
const Screen &display(ostream &os) const
{
do_display(os);
return *this;
}
private:
void do_display(ostream &os) const
{
os << contents;
}
pos x = 10;
pos y = 10;
pos curson = 0;
string contents;
};
#endif //SCREEN_DATA_H
#include "screen.h"
Screen &Screen::move(pos w, pos h)
{
curson = w * x + h;
return *this;
}
Screen &Screen::set(char c)
{
contents[curson] = c;
return *this;
}
Screen &Screen::set(pos w, pos h, char c)
{
contents[w * x + h] = c;
return *this;
}
#ifndef MGR_SCREEN_H
#define MGR_SCREEN_H
#include <vector>
#include "screen.h"
using std::vector;
class Mgr_screen
{
public:
using screensIndex = vector<Screen>::size_type;
void clean(screensIndex);
screensIndex addScreen(Screen &s);
private:
vector<Screen> screens{Screen(25,8,'X')};
};
#endif //MGR_SCREEN_H
#include "mgr_screen.h"
void Mgr_screen::clean(screensIndex i)
{
Screen &s = screens[i];
s.contents = string(s.x * s.y, 'M');
}
//返回类型是的作用域位于 Mgr_screen 之外,必须指明作用域
//名字查找(name lookup)寻找与所用名字最匹配的声明
//1>在名字所在块中寻找,只考虑名字出现之前的声明
//2>找不到,查找外层作用域
//3>找不到,报错
// 类的定义:
// 1>先编译成员的声明
// 2>直到类全部可见才编译函数体
// 所以成员函数可以使用类中定义的任何名字
Mgr_screen::screensIndex Mgr_screen::addScreen(Screen &s)
{
screens.push_back(s);
return screens.size() - 1;
}
#include "screen.h"
int main()
{
Screen myscreen(5,5,'O');
myscreen.move(4,0).set('#').display(cout);
cout << endl;
myscreen.display(cout);
cout << endl;
}