標籤:c++程式設計原理與實踐 讀書筆記
一個顯示模型
一種圖形及圖形化使用者介面直接對應螢幕顯示的思想。其基本概念先天就是圖形化的(而且都是二維的,適應電腦螢幕的矩形地區),這些基本概念包括座標、線、矩形和圓等。從編程的角度看,其目的是建立記憶體中的對象和螢幕映像的直接對應關係。
其基本模型如下:我們利用圖形系統提供的基本對象(如線)組合出更複雜的對象;然後將這些對象添加到一個表示物理螢幕的視窗對象中;最後,用一個程式將我們添加到視窗上的對象顯示在螢幕上,我們可以將這個程式看做螢幕顯示本身,或者是一個“顯示引擎”,或者是“我們的圖形庫”,或者“GUI庫”,甚至將其看做“在螢幕背後進行畫圖工作的小矮人”,然後在螢幕上畫出我們要添加到視窗的對象。
#include "Simple_windows.h"#include "Graph.h"int main(){ using namespace Graph_lib;Point tl(100,100);Simple_windows win(tl, 600,400, "Canvas");Polygon poly;poly.add(Point(300,200));poly.add(Point(350,100));poly.add(Point(400,200));poly.set_color(Color::red);win.attach(poly);win.wait_for_button();}
座標系
電腦螢幕是一個由像素組成的矩形地區,像素是一個可以設定為某種顏色的點。在程式中,最常見的方式就是將螢幕建模為由像素組成的矩形地區,每個像素由x(水平)座標和y(垂直)座標確定。最左端的像素的x座標為0,向右逐步遞增,直到最右端的像素為止:最頂端的像素y座標為0,向下逐步遞增,直到最底端的像素為止。注意,y座標是“向下增長”的。這可能有點奇怪,特別是對數學家而言。但是,“螢幕(視窗)大小各異,左上方可能是不同螢幕的唯一共同之處了,因些將其設定為原點。
按照慣例,main()函數包含我們要(直接或間接)執行的代碼及例外處理:
int main(){ try { //... here is our code... } catch(exception& e) { // some error reporting return 1; } catch(...) { // some more error reporting return 2; }}
為了使此main()編譯能過,我需要定義exception。如果像往常一樣包含了標頭檔std lib facilities.h或者直接包含標準庫標頭檔<stdexcept>,就會獲得exception的定義。
座標軸
Axis xa(Axis::x, Point(20,300),280,10, "x axis"); win.attach(xa);win.set_label("Canvas #2");win.wait_for_button();
具體操作步驟為:建立座標軸對象,將其添加到視窗,最後進行顯示,如右圖所示。可以看到,Axis::x是一條水平線,其上有指定數量(10個)的刻度和一個標籤"x axis"。通常,標籤用於解釋座標軸和刻度的含義。
繪製函數圖
現在,我們已經有了一個包含座標軸的視窗,因此看起來畫出一個函數是個好主意。我們建立一個形狀來表示正弦函數,並將它添加到視窗:
function sine(sin, 0, 100, Point(20,150),1000,50,50);win.attach(sine);win.set_label("Canvas #4");win.wait_for_button();
在這段代碼中,名為sine的function對象使用標準庫函數sin()產生的值繪製一條正弦曲線。
Polygon函數圖是表示資料的一種方法。Polygon(多邊形)描述為一個點的序列,這些點通過線串連起來就構成Polygon類。第一條線串連第一個點到第二個點,第二條線串連第二個點到第三個點......最後一條線串連最後一個點到第一個點:
sine.set_color(Color::blue);Polygon poly;poly.add(Point(300,200));poly.add(Point(350,100));poly.add(Point(400,200));poly.set_color(Color::red);poly.set_style(Line_style::dash);win.attach(poly);win.set_label("Canvas #5);win.wait_for_button();
螢幕是矩形,視窗是矩形,一張紙也是一個矩形。實際上,現實世界中很多形狀都是矩形(或者至少是圓角矩形),原因在於矩形是最容易處理的形狀。
Rectangle r(Point(200,200),100,50);
填充
前面繪製形狀都是繪製輪廓,我們也可以使用某種顏色“填充”形狀:
r.set_fill_color(Color::yellow);poly.set_style(Line_style(Line_style::dash,4));
任何封閉的形狀都可以填充。矩形很特殊,它非常容易填充。
文本
最後,任何一個繪圖系統都不可能完全沒有簡單的文本輸出方式 --將每個字元看做線的集合來繪製,並保證不會剪下掉字元。
Text t(Point(150,150), "Hello, graphical world!");
利用此例中的基本圖形元素,你可以產生任何複雜、微妙的顯示效果。請注意本章所有代碼的一個共同特點:沒有迴圈和選擇語句,而且所有資料都是“硬式編碼”。輸出內容只是基本圖形元素的簡單組合。一旦我們開始使用資料和演算法來組合這些基本圖形,就可以得到更複雜、更有趣的輸出效果了。
我們還可以從檔案中載入圖片:
Image ii(Point(100,50), "image.jpg");
基類和衍生類別
讓我們從一個更為技術性的角度來觀察基類和衍生類別。當設計一個圖形介面庫時,我們依賴以下3個關鍵的語言機制:
(一)派生(derivation):從一個類構造另一個類的方法,使新構造的類可以替換原來的類。衍生類別除了自己的成員外,還包括基類的所有成員。這通常稱為繼承,因為衍生類別“繼承”了其基類的所有成員。在某些上下文環境中,衍生類別稱為子類,而基類稱為父類。
(二)虛函數(virtual function):在基類中定義一個函數,在衍生類別中有一個類型和名稱完全一樣的函數,當使用者調用基類函數時,實際上調用的是衍生類別中的函數。
(三)私人和保護成員(Private and protected member):我們保持類的實現細節為私人的,以保護它們不被直接存取,簡化維護操作,這通常稱為封裝。
繼承、運行時多態和封裝的使用,實際上就是物件導向程式設計最常見的標誌。因此,除了其他的程式設計網路之外,C++還直接支援物件導向程式設計。
物件版面配置
對象在內在中是如何布局的呢。一個類的成員定義了對象的布局:資料成員在記憶體中一個接一個地儲存。當使用繼承時,衍生類別的資料成員被簡單地放在基類的成員之後。
類的派生和虛函數的定義
我們通過在類名後給出一個基類來指定一個類為衍生類別。
struct Circle::Shape { /*....*/};
預設情況下,結構體(struct)的成員都是公有的。基類中的公用成員也會成為結構體的公有成員。另一種等價的定義方式如下:
class Circle:public Shape {public:/*......*/};
一個虛函數必須在類的聲明中被聲明為virtual,但是如果你把函數定義放在類外,關鍵字virtual就不必且不能出現在那裡了。例如:
struct Shape{ //... virtual void draw_lines() const; virtual void movd(); //...};virtual void Shape::draw_lines() const {/**.../} //errorvoid Shape::move() {/*...*/} //OK
當你希望覆蓋一個虛函數時,必須使用與基類中完全相同的名字和類型。例如:
struct Circle: Shape { void draw_lines(int) const;void drawlines() const;void draw_lines();//...}
訪問
C++為類成員訪問提供了一個簡單的模型。類的成員可以是:
(一)私人的:如果一個成員是私人的,它的名字只能被其所屬類的成員使用。
(二)受保護的(protected):如果一個成員是受保護的,它的名字只能被其所屬類及其衍生類別的成員使用。
(三)公有的(public):如果一個成員是公有的,它的名字可以被所有函數使用。
純虛函數
一個抽象類別是一個只能作為基類的類。我們使用抽象類別來表示那些抽象的概念,即相關實體共性的一般化所對應的那些概念。在程式中,抽象類別通常定義了一組相關類(類層次)的介面。
《C++程式設計原理與實踐》讀書筆記(三)