Почему связанные списки использовать указатели вместо сохранения узлов внутри узлов

Я'вэ работал с связанные списки, прежде чем широко в Java, но я'м очень новой для C++. Я использовал этот класс узла, который был дан мне в проект, просто отлично

class Node
{
  public:
   Node(int data);

   int m_data;
   Node *m_next;
};

но у меня один вопрос, что это'т очень хорошо ответил. Почему это надо использовать

Node *m_next;

чтобы он указывал на следующий узел в списке вместо

Node m_next;

Я понимаю, что лучше использовать указатель версию; Я'м не собираюсь спорить с фактами, но я Дон'т знаю, почему это'ы лучше. У меня не так, внятного ответа о том, как лучше указатель для выделения памяти, и я был интересно, если кто может помочь мне понять, что лучше.

Комментарии к вопросу (22)
Решение

Это's не просто лучше, это'ы единственно возможным способом.

Если вы сохранили "узел" объект внутри себя, что бы оператор sizeof(узел)быть? Это будетоператор sizeof(тип int) + оператор sizeof(узел), которая будет равна оператор sizeof(тип int) + (оператор sizeof(тип int) + оператор sizeof(узел)), которая будет равнаоператор sizeof(тип int) + (оператор sizeof(тип int) + (оператор sizeof(тип int) + оператор sizeof(узел))) и т. д. до бесконечности.

Объект, как, что может'т существуют. Это'ы невозможно.

Комментарии (19)

В Java

Node m_node

хранит указатель на другой узел. Вы Don't есть выбор об этом. В C++

Node *m_node

означает то же самое. Разница в том, что в C++ вы можете хранить объект, а не указатель на него. Что's, почему вы должны сказать, вы хотите, чтобы указатель. В C++:

Node m_node

значит магазин узел прямо здесь (и, что очевидно, может't работа для список - вы в конечном итоге с рекурсивно определенной структурой).

Комментарии (10)

C++ это не JAVA. Когда вы пишете

Node m_next;

в Java, это все равно, что писать

Node* m_next;

в языке C++. В Java, указатель подразумевается, в C++ это явно. Если вы пишете

Node m_next;

в C++, вы помещаете экземпляр узел сейчас внутри объекта, который вы определяете. Она всегда есть и не может быть опущен, она не может быть распределена с "новым" и он не может быть удален. Такого эффекта невозможно достичь в Java, и это совершенно отличается от того, что Ява делает один и тот же синтаксис.

Комментарии (2)

Вы используете указатель, в противном случае ваш код:

class Node
{
   //etc
   Node m_next; //non-pointer
};

...бы не компиляции, поскольку компилятор не может вычислить размер "узел". Это потому, что это зависит от самого усилителя;amp; mdash; не значит, компилятор не может решить, сколько памяти она будет потреблять.

Комментарии (5)

Последний (узла m_next) должны содержать узел. Это не'т указать на это. И тогда не было бы никаких связывающих элементов.

Комментарии (3)

Подход, который вы описали совместима не только с C++, но и С [(в основном) подмножество языка C][1]. Научиться разрабатывать на C-стиль связанного списка является хорошим способом, чтобы представить себя низкоуровневые методы программирования (например, ручного управления памятью), но это вообще не рекомендуется для современной разработке на C++.

Ниже я реализовал четыре вариации на том, как управлять список элементов в C++.

  1. raw_pointer_demo использует тот же подход как у тебя -- ручное управление памяти, необходимой при использовании сырых указателей. Использование C++ здесь только для [синтаксического сахара][2], и применяемые подходы несовместимы с языка Си.
  2. В shared_pointer_demo` управление список до сих пор осуществляется вручную, а управление памятью [автоматическая][3] (Не'т использовать сырые указатели). Это очень похоже на то, что вы, вероятно, испытали с Java.
  3. std_list_demo использует стандартные библиотеки ["список"] [4] контейнер. Это показывает, насколько легче вам, если вы опираетесь на существующие библиотеки, а не свертывать ваши собственные.
  4. std_vector_demo использует стандартные библиотеки [вектор][5] контейнер. Это управляет списком хранение в одном непрерывном распределении память. Другими словами, Нет'т указатели на отдельные элементы. Для некоторых, довольно крайних случаях, это может стать существенно неэффективным. В типичных случаях, однако, [это рекомендуется для управления списками в C++][6].

Примечание: из всех этих, только raw_pointer_demo на самом деле требует, что список будет явно уничтожен во избежание, что "утечка" Память. Три другие методы автоматически уничтожить список и его содержимое, когда контейнер выходит из области видимости (в конце функции). Суть вот в чем: С++ может быть очень "на Java-подобном" в этом отношении, - но только если вы решите разработать свою программу, используя высокоуровневые инструменты в вашем распоряжении.


/*BINFMTCXX: -Wall -Werror -std=c++11
*/

#include 
#include 
#include 
#include 
#include 
#include 
using std::cerr;


/** Brief   Create a list, show it, then destroy it */
void raw_pointer_demo()
{
    cerr 
Комментарии (1)

Обзор

Существует 2 способа ведения и выделять объекты в C++, а на Java есть только один способ.

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

1.1 с++ без указателей

class AddressClass
{
  public:
    int      Code;
    char[50] Street;
    char[10] Number;
    char[50] POBox;
    char[50] City;
    char[50] State;
    char[50] Country;
};

class CustomerClass
{
  public:
    int          Code;
    char[50]     FirstName;
    char[50]     LastName;
    // "Address" IS NOT A pointer !!!
    AddressClass Address;
};

int main(...)
{
   CustomerClass MyCustomer();
     MyCustomer.Code = 1;
     strcpy(MyCustomer.FirstName, "John");
     strcpy(MyCustomer.LastName, "Doe");
     MyCustomer.Address.Code = 2;
     strcpy(MyCustomer.Address.Street, "Blue River");
     strcpy(MyCustomer.Address.Number, "2231 A");

   return 0;
} // int main (...)

.......................................
..+---------------------------------+..
..|          AddressClass           |..
..+---------------------------------+..
..| [+] int:      Code              |..
..| [+] char[50]: Street            |..
..| [+] char[10]: Number            |..
..| [+] char[50]: POBox             |..
..| [+] char[50]: City              |..
..| [+] char[50]: State             |..
..| [+] char[50]: Country           |..
..+---------------------------------+..
.......................................
..+---------------------------------+..
..|          CustomerClass          |..
..+---------------------------------+..
..| [+] int:      Code              |..
..| [+] char[50]: FirstName         |..
..| [+] char[50]: LastName          |..
..+---------------------------------+..
..| [+] AddressClass: Address       |..
..| +-----------------------------+ |..
..| | [+] int:      Code          | |..
..| | [+] char[50]: Street        | |..
..| | [+] char[10]: Number        | |..
..| | [+] char[50]: POBox         | |..
..| | [+] char[50]: City          | |..
..| | [+] char[50]: State         | |..
..| | [+] char[50]: Country       | |..
..| +-----------------------------+ |..
..+---------------------------------+..
.......................................

Предупреждение: синтаксис языка C++, используемый в этом примере, аналогичен синтаксису на Java. Но, выделение памяти другая.

1.2 В C++ элементы, используя указатели

class AddressClass
{
  public:
    int      Code;
    char[50] Street;
    char[10] Number;
    char[50] POBox;
    char[50] City;
    char[50] State;
    char[50] Country;
};

class CustomerClass
{
  public:
    int           Code;
    char[50]      FirstName;
    char[50]      LastName;
    // "Address" IS A pointer !!!
    AddressClass* Address;
};

.......................................
..+-----------------------------+......
..|        AddressClass         +Code = 1;
     strcpy(MyCustomer->FirstName, "John");
     strcpy(MyCustomer->LastName, "Doe");

     AddressClass* MyCustomer->Address = new AddressClass();
     MyCustomer->Address->Code = 2;
     strcpy(MyCustomer->Address->Street, "Blue River");
     strcpy(MyCustomer->Address->Number, "2231 A");

     free MyCustomer->Address();
     free MyCustomer();

   return 0;
} // int main (...)

Если вы увидеть разницу между обе стороны, вы'увидите, что в первой методике, пункт-адрес выделяется в рамках поддержки, в то время как второй способ, вы должны создать каждый адрес явным образом.

Предупреждение: в Java выделяет объекты в памяти, как это второй способ., но синтаксис, как и первый способ, который может ввести в заблуждение новичков, чтобы "в++" и

Реализации

Так что пример ваш список может быть что-то похожее на следующем примере.


class Node
{
  public:
   Node(int data);

   int m_data;
   Node *m_next;
};

.......................................
..+-----------------------------+......
..|            Node             |......
..+-----------------------------+......
..| [+] int:           m_data   |......
..| [+] Node*:         m_next   +---+..
..+-----------------------------+...|..
....................................|..
..+-----------------------------+...|..
..|            Node             +
Комментарии (0)

На стороне записки, если первый член класса или структуры следующим указателем (так что никаких виртуальных функций или любой другой функции класса, который будет означать рядом это't первый член класса или структуры), то вы можете использовать и"база" Класс или структура с рядом указатель, и использовать общий код для основных связанного списка операций, как добавление, вставка и прежде, получать от фронта, ... . Это потому, что C / С++ гарантирует, что адрес первого члена класса или структуры так же, как Адреса класса или структуры. Базовый узел Class или struct будет иметь только указатель Next, которые будут использоваться базовые функции связанного списка, затем типажей будут использоваться по мере необходимости, чтобы конвертировать между базовым типом узла и "производные" и типы узлов. Примечание - в C++, если базовый класс узел имеет только указатель Next, то я предполагаю, что производные классы могут'т иметь виртуальные функции.

Комментарии (0)

почему лучше использовать указатели в связанный список?

Причина в том, что при создании "узел" объекта, компилятор должен выделить память для этого объекта и размер объекта рассчитывается. Размер указателя на любой тип известен компилятору и поэтому с самоопределяемый размер указателя объекта может быть рассчитана.

Если узел m_nodeиспользуется, то компилятор не имеет понятия о размерах "узел" и она застряла в *бесконечная рекурсия* расчетаоператор sizeof(узел)`. Всегда помните: класс не может содержать членов своего типа.

Комментарии (0)

Потому что это на Си++

int main (..)
{
    MyClass myObject;

    // or

    MyClass * myObjectPointer = new MyClass();

    ..
}

эквивалентно этому в Ява

public static void main (..)
{
    MyClass myObjectReference = new MyClass();
}

где оба они создают новый объект класса MyClass, используя конструктор по умолчанию.

Комментарии (0)