c++ primer(第五版)筆記 第七章 類初探

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




發佈了41 篇原創文章 · 獲贊 1 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章