資料結構

我們已經瞭解瞭如何在C++中使用一組順序資料。但這有些侷限,因為在很多情況下,我們想儲存的不是僅僅是所有資料型別相同的元素序列,而是包含不同資料型別的不同元素集合。

資料結構

資料結構是將一組資料元素歸類到一個名稱下。這些被稱為成員的資料元素可以有不同的型別和不同的長度。資料結構在C++中使用以下語法宣告

struct 結構名 {
成員型別1 成員名1;
成員型別2 成員名2;
成員型別3 成員名3;
.
.
} 物件名;

,其中結構名是對結構型別的命名;物件名可以是一組有效的識別符號,用於表示該結構的物件的名稱。在大括號內部{ }是一個數據成員列表,每個成員都指定了型別和有效的識別符號作為其名稱。

首先我們需要知道的是,資料結構會建立一個新的型別:一旦聲明瞭一個數據結構,一個以指定的識別符號命名的結構名新型別就會被建立,並在程式的其餘部分像使用其他任何型別一樣使用它。例如

1
2
3
4
5
6
7
struct product {
  int weight;
  float price;
} ;

product apple;
product banana, melon;

我們首先聲明瞭一個名為product的結構型別,它有兩個成員weightprice,每個成員都是不同的基本型別。然後我們使用這個結構型別名稱(product)聲明瞭三個該型別的物件apple, bananamelon,就像我們對任何基本資料型別所做的那樣。

宣告之後,product就變成了一個像基本型別int, charshort一樣有效的新型別名稱,從那時起我們就可以像我們已經做的那樣宣告這個複合新型別的物件(變數)了。apple, bananamelon.

struct宣告的末尾,在結束分號之前,我們可以使用可選欄位物件名來直接宣告結構型別的物件。例如,我們也可以透過這種方式在定義資料結構型別的同時宣告結構物件apple, bananamelon很重要的一點是,要清楚地區分結構型別名稱和擁有該結構型別的物件(變數)。我們可以從一個結構型別(

1
2
3
4
struct product {
  int weight;
  float price;
} apple, banana, melon;

)。apple, bananamelon)例項化許多物件(即變數,例如product).

一旦我們聲明瞭特定結構型別(apple, bananamelon)的三個物件,我們就可以直接操作它們的成員。要做到這一點,我們使用一個點(.),插入在物件名稱和成員名稱之間。例如,我們可以像操作標準變數一樣操作這些元素中的任何一個,它們具有各自的型別

1
2
3
4
5
6
apple.weight
apple.price
banana.weight
banana.price
melon.weight
melon.

其中每一個都具有它們所引用的成員對應的的資料型別apple.weight, banana.weightmelon.weightjint指向的是一個型別為apple.price, banana.pricemelon.pricejfloat.

讓我們看一個真實的例子,您可以看到結構型別如何與基本型別一樣使用

// example about structures
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

struct movies_t {
  string title;
  int year;
} mine, yours;

void printmovie (movies_t movie);

int main ()
{
  string mystr;

  mine.title = "2001 A Space Odyssey";
  mine.year = 1968;

  cout << "Enter title: ";
  getline (cin,yours.title);
  cout << "Enter year: ";
  getline (cin,mystr);
  stringstream(mystr) >> yours.year;

  cout << "My favorite movie is:\n ";
  printmovie (mine);
  cout << "And yours is:\n ";
  printmovie (yours);
  return 0;
}

void printmovie (movies_t movie)
{
  cout << movie.title;
  cout << " (" << movie.year << ")\n";
}
Enter title: Alien
Enter year: 1979

My favorite movie is:
 2001 A Space Odyssey (1968)
And yours is:
 Alien (1979)

這個例子展示了我們如何像普通變數一樣使用物件成員。例如,成員yours.year是型別intmine.title是型別string.

的一個有效變數。mineyours物件也可以被視為型別movies_t的有效變數,例如,我們已將它們傳遞給函式printmovie,就像我們對常規變數所做的那樣。因此,資料結構最顯著的優點之一是,我們可以單獨引用它們的成員,或者將整個結構作為一個塊,僅用一個識別符號來引用。

資料結構是表示資料庫的特性,特別是如果我們考慮到構建資料結構陣列的可能性。

// array of structures
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

#define N_MOVIES 3

struct movies_t {
  string title;
  int year;
} films [N_MOVIES];

void printmovie (movies_t movie);

int main ()
{
  string mystr;
  int n;

  for (n=0; n<N_MOVIES; n++)
  {
    cout << "Enter title: ";
    getline (cin,films[n].title);
    cout << "Enter year: ";
    getline (cin,mystr);
    stringstream(mystr) >> films[n].year;
  }

  cout << "\nYou have entered these movies:\n";
  for (n=0; n<N_MOVIES; n++)
    printmovie (films[n]);
  return 0;
}

void printmovie (movies_t movie)
{
  cout << movie.title;
  cout << " (" << movie.year << ")\n";
}
Enter title: Blade Runner
Enter year: 1982
Enter title: Matrix
Enter year: 1999
Enter title: Taxi Driver
Enter year: 1976
 
You have entered these movies:
Blade Runner (1982)
Matrix (1999)
Taxi Driver (1976)

結構指標

與其他任何型別一樣,結構也可以由其自身的指標指向

1
2
3
4
5
6
7
struct movies_t {
  string title;
  int year;
};

movies_t amovie;
movies_t * pmovie;

在這裡,amovie是一個結構型別的物件,movies_tpmovie是指向結構型別物件的指標。因此,以下程式碼也是有效的movies_t指標的值

1
pmovie = &amovie;

將被分配給物件pmovie的引用(其記憶體地址)。amovie現在我們來看另一個包含指標的例子,它將用於介紹一個新的運算子:箭頭運算子(

)。->):

// pointers to structures
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

struct movies_t {
  string title;
  int year;
};

int main ()
{
  string mystr;

  movies_t amovie;
  movies_t * pmovie;
  pmovie = &amovie;

  cout << "Enter title: ";
  getline (cin, pmovie->title);
  cout << "Enter year: ";
  getline (cin, mystr);
  (stringstream) mystr >> pmovie->year;

  cout << "\nYou have entered:\n";
  cout << pmovie->title;
  cout << " (" << pmovie->year << ")\n";

  return 0;
}
Enter title: Invasion of the body snatchers
Enter year: 1978
 
You have entered:
Invasion of the body snatchers (1978)

前面的程式碼包含一個重要的介紹:箭頭運算子(->)。這是一個解引用運算子,專門用於指向有成員的物件的指標。該運算子用於訪問我們擁有其引用的物件的成員。在例子中,我們使用了

1
pmovie->

,這在所有方面都等同於

1
(*pmovie).

。這兩個表示式pmovie->title(*pmovie).title都是有效的,並且都表示我們正在計算由名為的指標指向的結構資料結構的成員titlepmovie。必須清楚地區分它和

1
*pmovie.

,它等同於

1
*(pmovie.title)

,並且會訪問名為的指標指向的結構資料結構的成員的結構物件(在本例中不是指標)的指標成員所指向的值。pmovie下面的面板總結了指標和結構成員的可能組合。

表示式評估內容等價表示
a.b物件 a 的成員 b
a->b指標 a 所指向物件的成員 b(*a).b
*a.b物件 a 的成員 b 所指向的值*(a.b)

巢狀結構


結構也可以巢狀,這樣結構的一個有效元素本身也可以是另一個結構。

1
2
3
4
5
6
7
8
9
10
11
12
struct movies_t {
  string title;
  int year;
};

struct friends_t {
  string name;
  string email;
  movies_t favorite_movie;
  } charlie, maria;

friends_t * pfriends = &charlie;

在之前的宣告之後,我們可以使用以下任何表示式

1
2
3
4
charlie.name
maria.favorite_movie.title
charlie.favorite_movie.year
pfriends->favorite_movie.
(順便說一下,最後兩個表示式指向同一個成員)。
Index
目錄