PVOID - Структуры данных

Структуры данных

 Этот раздел является переводом туториала C++ Language


   Структура данных это группа элементов данных, объединенных под одним именем. Эти элементы данных, известные как члены структуры данных или поля, могут иметь различные типы и различную длину. Структуры данных могут быть объявлены в C++ с использованием следующего синтаксиса:

struct type_name {

member_type1 member_name1;

member_type2 member_name2;

member_type3 member_name3;

.

.

} object_names;

где type_name это имя типа структуры, object_names это набор корректных идентификаторов для объектов, которые будут иметь тип этой структуры. Внутри фигурных скобок { } находится список полей, каждое из них определяется типом и именем, являющимся корректным идентификатором. Например:

struct product {
  int weight;
  double price;
} ;

product apple;
product banana, melon;

здесь объявляется тип данных product и определяется, что он имеет два поля: weight и price, причем эти поля разных фундаментальных типов. Это объявление создает новый тип (product), который затем используется для объявления трех объектов (переменных) этого типа: apple, banana и melon. Заметьте, что однажды объявленный тип product используется точно также, как и любой другой тип.

Справа после объявления структуры и перед завершающей точкой с запятой (;) может быть использовано опциональное поле object_names для непосредственного объявления объектов типа этой структуры. Например, объекты apple, banana и melon могут быть объявлены в момент объявления структуры данных:

struct product {
  int weight;
  double price;
} apple, banana, melon;

   В том случае, если имена объектов указаны, имя типа (product) становится опциональным: struct требует либо имя типа типа данных, либо хотя бы одно имя в списке объектов, но наличие и того и другого одновременно не требуется.

Важно четко понимать различие между именем типа структуры (product) и объектом этого типа (apple, banana и melon). Может быть объявлено множество объектов (такие как apple, banana и melon) одного типа данных (product).

Когда объявляются три объекта определенного типа структуры (apple, banana, and melon), к его членам можно получить доступ напрямую. Синтаксис для этого - просто поставить точку (.) между именем объекта и именем члена. Например, мы могли бы работать с любым из этих элементов, как если бы они были стандартными переменными своих соответствующих типов:

apple.weight
apple.price
banana.weight
banana.price
melon.weight
melon.price

   Каждый из этих элементов имеет тип данных, соответствующий типу члена структуры: apple.weight, banana.weight, и melon.weight имеют тип int, в то время как apple.price, banana.price, и melon.price имеют тип double.

Вот реальный пример со структурами в действии:

// пример со структурами
#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 является допустимой переменной типа int, а my.title является допустимой переменной типа string.

Но объекты mine и yours также являются переменными с типом (типа movies_t). Например, оба они были переданы в функцию printmovie, как если бы они были простыми переменными. Поэтому одной из особенностей структур данных является способность ссылаться как на своих членов по отдельности, так и на всю структуру в целом. В обоих случаях используется тот же идентификатор: имя структуры.

Поскольку структуры являются типами, они также могут использоваться как типы данных для массивов для построения таблиц или баз данных из них:

// массивы структур
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

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

void printmovie (movies_t movie);

int main ()
{
  string mystr;
  int n;

  for (n=0; n<3; 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<3; 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: The Matrix
Enter year: 1999
Enter title: Taxi Driver
Enter year: 1976
 
You have entered these movies:
Blade Runner (1982)
The Matrix (1999)
Taxi Driver (1976)

 

Указатели на структуры


Как и у любого другого типа, у структур существует свой собственный тип указателей.

struct movies_t {
  string title;
  int year;
};

movies_t amovie;
movies_t * pmovie;

   Здесь amovie это объект типа movies_t, а pmovie это указатель, который может указывать на объекты типа movies_t. Следовательно, следующий код также будет корректен:

pmovie = &amovie;

   Указателю pmovie будет присвоен адрес объекта amovie.

Теперь рассмотрим другой пример, который сочетает указатели и структуры и будет служить введению нового оператора: оператора стрелки (->):

// указатели на структуры
#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)


Оператор стрелка (->) - это оператор разыменовывания, который используется исключительно с указателями на объекты, имеющие члены. Этот оператор служит для доступа к членам объекта непосредственно через его адрес. Например, в примере выше:

pmovie->title

во всех случаях эквивалентен:

(*pmovie).title

   Оба выражения, pmovie->title и (*pmovie).title являются допустимыми, и оба обращаются к полю title структуры данных через указатель pmovie. Это определенно нечто иное, чем:

*pmovie.title

 что эквивалентно:

*(pmovie.title)

   Это предоставляет доступ к значению через гипотетическое поле title объекта-структуры pmovie (это не так, поскольку title не является указателем). Следующая таблица суммирует возможные комбинации операторов для указателей и для членов структуры:

 Выражение   Значение   Эквивалент 
 a.b  Член b объекта a   
 a->b  Член b объекта, на который указывает a   (*a).b
 *a.b  Значение, на которое указывает член b объекта a   *(a.b)

 

 Вложенные структуры


Структуры также могут быть вложены таким образом, что элемент структуры сам по себе является другой структурой:

struct movies_t {
  string title;
  int year;
};

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

friends_t * pfriends = &charlie;

После этого объявления все следующие выражения будут допустимы:

charlie.name
maria.favorite_movie.title
charlie.favorite_movie.year
pfriends->favorite_movie.year

(Причем последние два выражения относятся к одному и тому же элементу).