数据结构

我们已经了解了如何在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
目录