Я'вэ работал с связанные списки, прежде чем широко в Java, но я'м очень новой для C++. Я использовал этот класс узла, который был дан мне в проект, просто отлично
class Node
{
public:
Node(int data);
int m_data;
Node *m_next;
};
но у меня один вопрос, что это'т очень хорошо ответил. Почему это надо использовать
Node *m_next;
чтобы он указывал на следующий узел в списке вместо
Node m_next;
Я понимаю, что лучше использовать указатель версию; Я'м не собираюсь спорить с фактами, но я Дон'т знаю, почему это'ы лучше. У меня не так, внятного ответа о том, как лучше указатель для выделения памяти, и я был интересно, если кто может помочь мне понять, что лучше.
Это's не просто лучше, это'ы единственно возможным способом.
Если вы сохранили "узел" объект внутри себя, что бы оператор sizeof(узел)быть? Это будет
оператор sizeof(тип int) + оператор sizeof(узел), которая будет равна оператор sizeof(тип int) + (оператор sizeof(тип int) + оператор sizeof(узел)), которая будет равна
оператор sizeof(тип int) + (оператор sizeof(тип int) + (оператор sizeof(тип int) + оператор sizeof(узел))) и т. д. до бесконечности.
Объект, как, что может'т существуют. Это'ы невозможно.
В Java
Node m_node
хранит указатель на другой узел. Вы Don't есть выбор об этом. В C++
Node *m_node
означает то же самое. Разница в том, что в C++ вы можете хранить объект, а не указатель на него. Что's, почему вы должны сказать, вы хотите, чтобы указатель. В C++:
Node m_node
значит магазин узел прямо здесь (и, что очевидно, может't работа для список - вы в конечном итоге с рекурсивно определенной структурой).
C++ это не JAVA. Когда вы пишете
Node m_next;
в Java, это все равно, что писать
Node* m_next;
в языке C++. В Java, указатель подразумевается, в C++ это явно. Если вы пишете
Node m_next;
в C++, вы помещаете экземпляр узел
сейчас внутри объекта, который вы определяете. Она всегда есть и не может быть опущен, она не может быть распределена с "новым" и он не может быть удален. Такого эффекта невозможно достичь в Java, и это совершенно отличается от того, что Ява делает один и тот же синтаксис.
Вы используете указатель, в противном случае ваш код:
class Node
{
//etc
Node m_next; //non-pointer
};
...бы не компиляции, поскольку компилятор не может вычислить размер "узел". Это потому, что это зависит от самого усилителя;amp; mdash; не значит, компилятор не может решить, сколько памяти она будет потреблять.
Подход, который вы описали совместима не только с C++, но и С (в основном) подмножество языка C. Научиться разрабатывать на C-стиль связанного списка является хорошим способом, чтобы представить себя низкоуровневые методы программирования (например, ручного управления памятью), но это вообще не рекомендуется для современной разработке на C++.
Ниже я реализовал четыре вариации на том, как управлять список элементов в C++.
raw_pointer_demo
использует тот же подход как у тебя -- ручное управление памяти, необходимой при использовании сырых указателей. Использование C++ здесь только для синтаксического сахара, и применяемые подходы несовместимы с языка Си.std_list_demo
использует стандартные библиотеки "список" контейнер. Это показывает, насколько легче вам, если вы опираетесь на существующие библиотеки, а не свертывать ваши собственные.std_vector_demo
использует стандартные библиотеки вектор
контейнер. Это управляет списком хранение в одном непрерывном распределении память. Другими словами, Нет'т указатели на отдельные элементы. Для некоторых, довольно крайних случаях, это может стать существенно неэффективным. В типичных случаях, однако, это рекомендуется для управления списками в C++.Примечание: из всех этих, только raw_pointer_demo
на самом деле требует, что список будет явно уничтожен во избежание, что "утечка" Память. Три другие методы автоматически уничтожить список и его содержимое, когда контейнер выходит из области видимости (в конце функции). Суть вот в чем: С++ может быть очень "на Java-подобном" в этом отношении, - но только если вы решите разработать свою программу, используя высокоуровневые инструменты в вашем распоряжении.
/*BINFMTCXX: -Wall -Werror -std=c++11
*/
#include <iostream>
#include <algorithm>
#include <string>
#include <list>
#include <vector>
#include <memory>
using std::cerr;
/** Brief Create a list, show it, then destroy it */
void raw_pointer_demo()
{
cerr << "\n" << "raw_pointer_demo()..." << "\n";
struct Node
{
Node(int data, Node *next) : data(data), next(next) {}
int data;
Node *next;
};
Node * items = 0;
items = new Node(1,items);
items = new Node(7,items);
items = new Node(3,items);
items = new Node(9,items);
for (Node *i = items; i != 0; i = i->next)
cerr << (i==items?"":", ") << i->data;
cerr << "\n";
// Erase the entire list
while (items) {
Node *temp = items;
items = items->next;
delete temp;
}
}
raw_pointer_demo()...
9, 3, 7, 1
/** Brief Create a list, show it, then destroy it */
void shared_pointer_demo()
{
cerr << "\n" << "shared_pointer_demo()..." << "\n";
struct Node; // Forward declaration of 'Node' required for typedef
typedef std::shared_ptr<Node> Node_reference;
struct Node
{
Node(int data, std::shared_ptr<Node> next ) : data(data), next(next) {}
int data;
Node_reference next;
};
Node_reference items = 0;
items.reset( new Node(1,items) );
items.reset( new Node(7,items) );
items.reset( new Node(3,items) );
items.reset( new Node(9,items) );
for (Node_reference i = items; i != 0; i = i->next)
cerr << (i==items?"":", ") << i->data;
cerr<<"\n";
// Erase the entire list
while (items)
items = items->next;
}
shared_pointer_demo()...
9, 3, 7, 1
/** Brief Show the contents of a standard container */
template< typename C >
void show(std::string const & msg, C const & container)
{
cerr << msg;
bool first = true;
for ( int i : container )
cerr << (first?" ":", ") << i, first = false;
cerr<<"\n";
}
/** Brief Create a list, manipulate it, then destroy it */
void std_list_demo()
{
cerr << "\n" << "std_list_demo()..." << "\n";
// Initial list of integers
std::list<int> items = { 9, 3, 7, 1 };
show( "A: ", items );
// Insert '8' before '3'
items.insert(std::find( items.begin(), items.end(), 3), 8);
show("B: ", items);
// Sort the list
items.sort();
show( "C: ", items);
// Erase '7'
items.erase(std::find(items.begin(), items.end(), 7));
show("D: ", items);
// Erase the entire list
items.clear();
show("E: ", items);
}
std_list_demo()...
A: 9, 3, 7, 1
B: 9, 8, 3, 7, 1
C: 1, 3, 7, 8, 9
D: 1, 3, 8, 9
E:
/** brief Create a list, manipulate it, then destroy it */
void std_vector_demo()
{
cerr << "\n" << "std_vector_demo()..." << "\n";
// Initial list of integers
std::vector<int> items = { 9, 3, 7, 1 };
show( "A: ", items );
// Insert '8' before '3'
items.insert(std::find(items.begin(), items.end(), 3), 8);
show( "B: ", items );
// Sort the list
sort(items.begin(), items.end());
show("C: ", items);
// Erase '7'
items.erase( std::find( items.begin(), items.end(), 7 ) );
show("D: ", items);
// Erase the entire list
items.clear();
show("E: ", items);
}
std_vector_demo()...
A: 9, 3, 7, 1
B: 9, 8, 3, 7, 1
C: 1, 3, 7, 8, 9
D: 1, 3, 8, 9
E:
int main()
{
raw_pointer_demo();
shared_pointer_demo();
std_list_demo();
std_vector_demo();
}
Обзор
Существует 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 +<--+..
..+-----------------------------+...|..
..| [+] 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 main(...)
{
CustomerClass* MyCustomer = new CustomerClass();
MyCustomer->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 +<--+..
..+-----------------------------+......
..| [+] int: m_data |......
..| [+] Node*: m_next +---+..
..+-----------------------------+...|..
....................................|..
..+-----------------------------+...|..
..| Node +<--+..
..+-----------------------------+......
..| [+] int: m_data |......
..| [+] Node*: m_next +---+..
..+-----------------------------+...|..
....................................v..
...................................[X].
.......................................
Резюме
Поскольку связанный список имеет переменное количество товаров, объем памяти выделяется, как это требуется, и, как доступно.
Обновление:
Также стоит отметить, как @haccks прокомментировал в своем посте.
Что иногда, ссылки или указатели на объект, указывать вложенные элементы (а.к.а. на "У. М. Л. композиция на").
И иногда, ссылки или указатели на объект, указывает на внешние элементы (а.к.а. на "У. М. Л. агрегации и").
Но, вложенные элементы того же класса, не могут быть применены с "нет-указатель на" Техника.
На стороне записки, если первый член класса или структуры следующим указателем (так что никаких виртуальных функций или любой другой функции класса, который будет означать рядом это't первый член класса или структуры), то вы можете использовать и"база" Класс или структура с рядом указатель, и использовать общий код для основных связанного списка операций, как добавление, вставка и прежде, получать от фронта, ... . Это потому, что C / С++ гарантирует, что адрес первого члена класса или структуры так же, как Адреса класса или структуры. Базовый узел Class или struct будет иметь только указатель Next, которые будут использоваться базовые функции связанного списка, затем типажей будут использоваться по мере необходимости, чтобы конвертировать между базовым типом узла и "производные" и типы узлов. Примечание - в C++, если базовый класс узел имеет только указатель Next, то я предполагаю, что производные классы могут'т иметь виртуальные функции.
почему лучше использовать указатели в связанный список?
Причина в том, что при создании "узел" объекта, компилятор должен выделить память для этого объекта и размер объекта рассчитывается. Размер указателя на любой тип известен компилятору и поэтому с самоопределяемый размер указателя объекта может быть рассчитана.
Если узел m_nodeиспользуется, то компилятор не имеет понятия о размерах "узел" и она застряла в *бесконечная рекурсия* расчета
оператор sizeof(узел)`. Всегда помните: класс не может содержать членов своего типа.
Потому что это на Си++
int main (..)
{
MyClass myObject;
// or
MyClass * myObjectPointer = new MyClass();
..
}
эквивалентно этому в Ява
public static void main (..)
{
MyClass myObjectReference = new MyClass();
}
где оба они создают новый объект класса MyClass
, используя конструктор по умолчанию.