跳轉到內容

C++ 程式設計/結構

來自 Wikibooks,開放世界中的開放書籍

結構是一種複合資料型別,它包含不同型別和不同成員。成員可以透過其名稱進行訪問。結構物件的數值是物件中每個成員的數值的元組。結構也可以被看作是面向物件程式設計 (OOP) 中物件正規化的一種簡單實現。Astruct就像一個class 除了預設訪問許可權(類預設訪問許可權為 private,結構預設訪問許可權為 public)。C++ 還保證只包含 C 型別的結構等效於相同的 C 結構,從而允許訪問舊版 C 函式,它可以(但可能不會)也有建構函式(並且必須有它們,如果一個模板類被用在一個struct)中,與類一樣,如果結構沒有使用者宣告的解構函式,編譯器會隱式宣告一個解構函式。結構還將允許 運算子過載.

結構的定義

struct myStructType /*: inheritances */ {
public: 
 // declare public members here
protected:
 // declare protected members here
private:
 // declare private members here
};

可選關鍵字 public:protected:private: 宣告以下成員的保護狀態。它們可以以任何順序放置,每個關鍵字可以出現多次。如果沒有給出保護狀態,則成員為 public。(私有或受保護的成員只能被結構的方法或友元訪問;在後面的章節中解釋)。

由於它在 C 中不受支援,因此在 C++ 中使用繼承的結構並不常見,即使它們與類一樣受支援。更具特色的方面是結構可以具有兩種身份:一種是與型別相關的,另一種是與特定物件相關的。公共訪問標籤有時可以忽略,因為結構對成員函式和欄位的預設狀態是公共的。

型別為 myStructType 的物件是使用宣告

 /* struct */ myStructType obj1 /* , obj2, ... */;

在開頭重複關鍵字 struct 是可選的。

可以在結構定義中直接定義物件,而不是使用結構型別的名稱

 struct { /*members*/ } obj1 /*, obj2, .. */ ;


注意
從技術角度來看,結構和類實際上是相同的。結構可以在任何可以使用類的地方使用,反之亦然,唯一的技術區別是類成員預設是private,而結構成員預設是public。結構可以透過在結構開頭新增關鍵字 private 來使其像類一樣工作。除此之外,這主要是一個關於約定和程式設計設計的區別,通常表明是從 C 語言原始碼轉換而來,甚至用作結構不會被繼承或不會有函式成員的錯誤隱式指示(這種用法應該避免,並且永遠不要假設)。

為什麼要使用結構而不是類?

較舊的程式語言使用了一種類似的型別稱為記錄(例如:COBOL、FORTRAN),它在 C 中作為 struct 關鍵字實現。因此,C++ 使用結構來遵守 C 的傳統(程式碼和程式設計師)。結構更易於程式設計師和編譯器管理。應該使用一個struct對於 POD (PlainOldData) 型別,它們沒有方法,並且所有資料成員都是public. struct可能在預設情況下使用公共繼承(最常見的一種)以及public訪問(如果先列出公共介面,這就是你想要的)是預期效果。使用一個class,你通常需要在兩個地方插入關鍵字 public,但沒有真正的好處。最終這只是一個約定問題,程式設計師應該能夠習慣它。

點物件

作為一個簡單的複合結構示例,考慮數學點概念。從某種程度上來說,一個點是兩個數字(座標),我們將其作為一個整體對待。在數學符號中,點通常用括號寫,逗號分隔座標。例如,(0, 0) 表示原點,(x, y) 表示從原點向右 x 個單位,向上 y 個單位的點。

表示點的自然方法是使用兩個雙精度浮點數。結構struct是將這兩個值組合成複合物件的解決方案之一。

// A struct definition:
 struct Point { double x, y; };

這個定義表明這個結構包含兩個成員,名為xy。這些成員也稱為例項變數,我將在稍後解釋原因。

在結構定義末尾省略分號是一個常見的錯誤。在花括號後面放分號可能看起來很奇怪,但你會習慣的。這種語法是為了讓程式設計師在定義結構時能夠建立結構的例項。

定義了新結構後,就可以建立具有該型別的變數

struct Point blank; 
blank.x = 3.0; 
blank.y = 4.0;

第一行是傳統的變數宣告:blank 的型別是 Point。接下來的兩行初始化結構的例項變數。這裡使用的“點符號”類似於在物件上呼叫函式的語法,如fruit.length()。當然,一個區別是函式名後面總是跟著一個引數列表,即使它是空的。

像往常一樣,變數 blank 的名稱出現在框外面,它的值出現在框裡面。在這種情況下,該值是一個包含兩個命名例項變數的複合物件。

訪問例項變數

可以使用與寫入它們相同的語法來讀取例項變數的值

double x = blank.x;

表示式blank.x表示“轉到名為 blank 的物件,獲取名為 x 的成員的值”。在這種情況下,我們將該值賦給名為x的區域性變數。請注意,名為x的區域性變數與名為x的例項變數之間沒有衝突。點符號的目的是明確地識別你所指的變數。

可以使用點符號作為任何表示式的部分,因此以下內容是合法的。

cout << blank.x << ", " << blank.y << endl; 
double distance = sqrt(blank.x * blank.x + blank.y * blank.y);

第一行輸出 3, 4;第二行計算值為 5。

對結構的操作

我們在其他型別上使用的大多數運算子,例如數學運算子(+, %等)和比較運算子(==, >等),不適用於結構。實際上,可以為新型別定義這些運算子的含義,但我們在這本書中不會這樣做。

另一方面,賦值運算子對結構有效。它可以用兩種方式使用:初始化結構的例項變數或將一個結構的例項變數複製到另一個結構。初始化看起來像這樣

Point blank = { 3.0, 4.0 };

花括號中的值依次賦給結構的例項變數。因此,在這種情況下,x獲取第一個值,y 獲取第二個值。

不幸的是,這種語法只能用於初始化,而不能用於賦值語句。因此,以下是非法的。

Point blank; 
blank = { 3.0, 4.0 }; // WRONG !!

你可能想知道為什麼這個完全合理的語句應該是非法的,而且沒有很好的答案。(但是,請注意,自 1999 年以來,C 中存在類似的語法,並且正在考慮將其可能包含在未來的 C++ 中)。

另一方面,將一個結構賦給另一個結構是合法的。例如

Point p1 = { 3.0, 4.0 }; 
Point p2 = p1; 
cout << p2.x << ", " <<  p2.y << endl;

該程式的輸出為 3, 4

結構作為函式引數和返回型別

可以編寫接受或返回結構的函式。例如,findCenter 接受一個 Rectangle 作為引數,並返回一個包含 Rectangle 中心座標的 Point

struct Rectangle {
  Point corner;
  double width, height;
};

Point findCenter (const Rectangle& box) 
{ 
  double x = box.corner.x + box.width/2; 
  double y = box.corner.y + box.height/2; 
  Point result = {x, y}; 
  return result; 
}

要呼叫此函式,必須傳遞一個 Rectangle 作為引數,並將返回值賦給一個 Point 變數

Rectangle mybox = { {10.0, 0.0}, 100, 200 }; 
Point center = findCenter (mybox); 
printPoint (center);

該程式的輸出為 (60, 100)。

請注意,Rectangle 正在透過引用傳遞給函式 findCenter(在 chapter 函式中解釋),因為這比複製整個結構(如果按值傳遞會發生這種情況)更有效。引用宣告為常量,這意味著函式 findCenter 不會修改引數box,尤其是呼叫者的mybox 將保持不變。

指標和結構

結構也可以由指標指向並存儲指標。規則與任何基本資料型別相同。指標必須宣告為指向結構的指標。

巢狀結構

[編輯 | 編輯原始碼]

結構也可以巢狀,因此結構的有效元素也可以是另一個結構。

//of course you have to define the Point struct first!

struct Rectangle {
 Point upper_left; 
 Point upper_right;
 Point lower_left;
 Point lower_right;
};

this 關鍵字是一個隱式建立的指標,它只能在結構(或聯合或類)的非靜態成員函式中訪問,並指向呼叫成員函式的物件。此指標在靜態成員函式中不可用。這將在介紹聯合時再次重申,在 關於類部分中提供了更深入的分析。

華夏公益教科書