#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;
}