С++ указатель на класс
Может ли кто-нибудь сказать мне, в чем разница между ними:
Display *disp = new Display();
и
Display *disp;
disp = new Display();
и
Display* disp = new Display();
и
Display* disp(new Display());
Ответы
Ответ 1
Первый случай:
Display *disp = new Display();
Есть три вещи:
- Он создает новую переменную
disp
с типом Display*
, то есть указателем на объект типа Display
, а затем
- Он выделяет новый объект
Display
в куче и
- Он устанавливает переменную
disp
для указания на новый объект Display
.
Во втором случае:
Display *disp; disp = new GzDisplay();
Вы создаете переменную disp
с типом Display*
, а затем создаете объект с другим типом GzDisplay
в куче и назначаете его указатель на переменную disp
.
Это будет работать, только если GzDisplay является подклассом Display. В этом случае он выглядит как пример polymorphism.
Кроме того, чтобы ответить на ваш комментарий, нет разницы между объявлениями:
Display* disp;
и
Display *disp;
Однако из-за того, как работают правила типа C, существует разница между:
Display *disp1;
Display* disp2;
и
Display *disp1, disp2;
Потому что в этом последнем случае disp1
является указателем на объект Display
, вероятно, выделенным в куче, а disp2
является фактическим объектом, вероятно, выделенным в стеке. То есть, хотя указатель, возможно, является частью этого типа, парсер свяжет его с переменной.
Ответ 2
// implicit form
// 1) creates Display instance on the heap (allocates memory and call constructor with no arguments)
// 2) creates disp variable on the stack initialized with pointer to Display instance
Display *disp = new Display();
// explicit form
// 1) creates Display instance on the heap (allocates memory and call constructor with no arguments)
// 2) creates disp variable on the stack initialized with pointer to Display instance
Display* disp(new Display());
// 1) creates uninitialized disp variable on the stack
// 2) creates Display instance on the heap (allocates memory and call constructor with no arguments)
// 3) assigns disp with pointer to Display instance
Display *disp;
disp = new Display();
Разница между явными и неявными формами инициализации будет видна только для сложных типов с конструкторами. Для типа указателя (Display *) нет разницы.
Чтобы увидеть разницу между явными и неявными формами, проверьте следующий пример:
#include <iostream>
class sss
{
public:
explicit sss( int ) { std::cout << "int" << std::endl; };
sss( double ) { std::cout << "double" << std::endl; };
// Do not write such classes. It is here only for teaching purposes.
};
int main()
{
sss ddd( 7 ); // prints int
sss xxx = 7; // prints double, because constructor with int is not accessible in implicit form
return 0;
}
Ответ 3
Display *disp = new Display();
Эта строка кода создает переменную типа Display * и инициализирует ее адресом только что созданного объекта.
Display *disp; // (1)
disp = new Display(); // (2)
Первая строка кода просто объявляет переменную типа Display *. В зависимости от настроек вашего компилятора - указатель может быть инициализирован или не быть инициализирован. В принципе, его следует рассматривать как недопустимый указатель, который не обязательно указывает на NULL.
Вторая строка присваивает указателю только что созданный объект.
Результат обоих фрагментов кода будет таким же.
Если включена оптимизация, любой компилятор должен сгенерировать одну и ту же сборку для обоих. При отключенных оптимизациях и при генерации некоторого кода отладки - оба фрагмента могут генерировать совершенно другой код - во втором случае указатель сначала будет инициализирован значением, используемым компилятором для неинициализированных указателей (что-то вроде 0xDEADBEEF или 0xEFEFEFEFEF, и легко узнаваемым шаблон). В первом фрагменте - указатель всегда должен быть инициализирован по адресу объекта независимо от настроек. Обратите внимание, что это зависит от компилятора - некоторые компиляторы могут делать, как я говорю, некоторые могут сделать что-то совершенно другое.
Ответ 4
Вы нашли четыре способа написать одно и то же.
Примеры 1 (Display *disp…
) и 3 (Display* disp…
) идентичны; расстояние между *
не имеет значения. Однако стиль 1 часто предпочтительнее, потому что:
Display* disp1, disp2;
на самом деле означает:
Display *disp1, disp2;
i.e, disp2 не является указателем.
Пример два (разделение на две строки) имеет тот же эффект и, вероятно, будет скомпилирован с тем же кодом. Четвертый пример, используя синтаксис инициализатора, делает то же самое.
Обратите внимание: если это классы, а не указатели, то может быть разница в поведении и скорости.
Ответ 5
Создается объект типа Display
и один из типов Gz Display
, это специально или это опечатка?
Если это опечатка, тогда нет никакой разницы в отношении сгенерированного кода, но первый метод является предпочтительным, поскольку нет времени, в течение которого переменная disp
находится в области видимости и неинициализирована.
Ответ 6
1) Экземпляр GzDisplay создается во втором варианте, тогда как в 1-м варианте созданный экземпляр имеет тип Display (я предполагаю, что GzDisplay является подклассом Display, правильно?);
2) Кроме того, 1-й более короткий, во 2-й disp имеет значение undefined, пока не будет назначен новый GzDisplay(). Второй вариант дает вам шанс случайно забыть об этом и вставить код, который использует disp до его инициализации.