В чем основная разница в создании объектов между Java и C++?

Я готовлюсь к экзамену по Java, и один из вопросов, который был на предыдущем экзамене, был: "Какая основная разница в создании объектов между Java и C++?"

Я думаю, что знаю основы создания объектов, например, как вызываются конструкторы и что делают блоки инициализации в Java, и что происходит, когда конструктор одного класса вызывает метод другого класса, который еще не создан, и так далее, но я могу не нахожу ничего очевидного. Предполагается, что ответ должен состоять из одного или двух предложений, поэтому я не думаю, что описание всего процесса создания объекта в Java — это то, что они имели в виду.

Любые идеи?


person AndrejaKo    schedule 29.09.2010    source источник
comment
Вы также можете найти этот вопрос полезным. stackoverflow .com/questions/4405074/   -  person Berat Baran Cevik    schedule 27.01.2016


Ответы (7)


В дополнение к другим отличным ответам, одна вещь очень важна и обычно игнорируется/забывается или неправильно понимается (что объясняет, почему я подробно описываю процесс ниже):

  • В Java методы являются виртуальными, даже если они вызываются из конструктора (что может привести к ошибкам)
  • В C++ виртуальные методы не являются виртуальными при вызове из конструктора (что может привести к неправильному пониманию)

Какие?

  • Давайте представим базовый класс с виртуальным методом foo().
  • Давайте представим класс Derived, наследующий от Base, который переопределяет метод foo().

Разница между С++ и Java:

  • В Java вызов foo() из конструктора базового класса вызовет Derived.foo()
  • В C++ вызов foo() из конструктора базового класса вызовет Base.foo()

Почему?

Ошибки для каждого языка разные:

  • В Java вызов любого метода в конструкторе может привести к тонким ошибкам, поскольку переопределенный виртуальный метод может попытаться получить доступ к переменной, которая была объявлена/инициализирована в классе Derived.

Концептуально работа конструктора состоит в том, чтобы создать объект (что вряд ли можно назвать обычным подвигом). Внутри любого конструктора весь объект может быть сформирован лишь частично — вы можете знать только, что объекты базового класса были инициализированы, но вы не можете знать, какие классы унаследованы от вас. Однако вызов метода с динамической привязкой достигает «вперед» или «вне» в иерархию наследования. Он вызывает метод в производном классе. Если вы делаете это внутри конструктора, вы вызываете метод, который может манипулировать еще не инициализированными членами — верный путь к катастрофе.

Брюс Экель, http://www.codeguru.com/java/tij/tij0082.shtml< /а>

  • В C++ следует помнить, что виртуальный объект не будет работать должным образом, так как будет вызываться только метод текущего сконструированного класса. Причина в том, чтобы избежать доступа к членам данных или даже методам, которые еще не существуют.

При построении базового класса виртуальные функции никогда не переходят в производные классы. Вместо этого объект ведет себя так, как если бы он был базового типа. Неформально говоря, при построении базового класса виртуальные функции не являются.

Скотт Мейерс, http://www.artima.com/cppsource/nevercall.html

person paercebal    schedule 29.09.2010
comment
+1 Я сам наткнулся на это... мои родители рассказывали мне о многих опасных вещах, но никогда не говорили мне об этом, и мне пришлось искать это в Интернете. - person Arc; 29.09.2010
comment
@Hemant & @Archimedix: Спасибо за ваши комментарии! Автор вопроса упомянул в своем вопросе виртуальную вещь, но я предположил, что проблема была достаточно коварной / порочной, чтобы иметь развернутый ответ, полный описания, фактов и источников, вместо того, чтобы ограничиваться половиной предложения в вопросе. Я рад видеть, что не ошибся в этом предположении. - person paercebal; 30.09.2010
comment
Я не могу сказать, какой ответ является лучшим для этого вопроса, поэтому я приму этот, потому что на данный момент он имеет наибольшее количество голосов. - person AndrejaKo; 03.10.2010

В чем основная разница в создании объектов между Java и C++?

В отличие от Java, в C++ объекты также можно создавать в стеке.

Например, на C++ вы можете написать

Class obj; //object created on the stack

В Java вы можете написать

Class obj; //obj is just a reference(not an object)
obj = new Class();// obj refers to the object
person Prasoon Saurav    schedule 29.09.2010
comment
Я бы сказал, что это выделение памяти, которое происходит до создания объекта. Это действительно считается? - person Björn Pollex; 29.09.2010
comment
Я не уверен, что они спрашивают о выделении памяти, но, с другой стороны, я не уверен, что они не спрашивают о выделении памяти. - person AndrejaKo; 29.09.2010
comment
Я бы расширил этот ответ на рассмотрение нового размещения ... С++ позволяет создавать объект где угодно - автоматически в стеке или куче или даже в указанной вами области памяти. В Java все это делает JVM... и всегда находится в куче. - person Deep-B; 29.09.2010
comment
@ Deep-B: Размещение new - очень продвинутая техника. Я бы не ожидал, что средний пользователь C++ будет знать, как его использовать, не говоря уже о студенте, изучающем разницу между C++ и Java. - person Martin York; 29.09.2010

Помимо проблем с кучей/стеком, я бы сказал: конструкторы С++ имеют списки инициализации, а Java использует присваивание. См. http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.6 для получения подробной информации.

person LumpN    schedule 29.09.2010
comment
+1 Это то, что при миграции с Java на C++ часто не понимают. - person fredoverflow; 29.09.2010

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

person Yakov Galka    schedule 29.09.2010
comment
То же, что и @Prasoon: выделение происходит до создания объекта, так действительно ли это то, о чем просят? - person Björn Pollex; 29.09.2010
comment
@Space: по крайней мере, в Java вы не можете выделить память без создания объекта (и даже в C ++ эти два обычно идут вместе), поэтому я бы сказал, что эта информация все еще актуальна. - person Joachim Sauer; 29.09.2010
comment
Ну, в отличие от C++, в java вы не можете отделить выделение от создания. - person Yakov Galka; 29.09.2010
comment
@Joachim: Этот комментарий сам по себе может быть очень хорошим ответом на этот вопрос. - person Björn Pollex; 29.09.2010
comment
@ Space_C0wb0y: я бы сказал, что выделение памяти является частью создания объекта, а другое искусство — инициализация. - person JeremyP; 29.09.2010

В Java виртуальная машина Java (JVM), которая выполняет код Java, должна может1 регистрировать все создаваемые объекты (или, если быть точным, ссылки на них), чтобы память выделенные для них, могут быть позже автоматически освобождены сборщиком мусора, когда на объекты больше не ссылаются.

EDIT: я не уверен, можно ли это отнести к созданию объекта в строгом смысле, но это определенно происходит между созданием и присвоением переменной, даже без явного присваивания (когда вы создаете объект без его назначения JVM должна автоматически освободить его через некоторое время после этого, поскольку ссылок больше нет).

В C++ только объекты, созданные в стеке, освобождаются автоматически (когда они выходят за пределы области видимости), если вы не используете какой-либо механизм, который обрабатывает это за вас.

1: в зависимости от реализации JVM.

person Arc    schedule 29.09.2010
comment
Это на самом деле зависит от реализации. Насколько мне известно, большинство сборщиков мусора будет проходить графы объектов из корня коллекции, а не увеличивать счетчик ссылок. - person Guillaume; 29.09.2010

Существует одно основное дизайн различие между конструкторами в C++ и Java. Из этого дизайнерского решения вытекают и другие отличия.

Основное отличие состоит в том, что JVM сначала инициализирует все элементы нулями, прежде чем начинать выполнение любого конструктора. В C++ инициализация члена является частью конструктора.

В результате во время выполнения конструктора базового класса в C++ члены производного класса еще не инициализированы! В Java они были инициализированы нулями.

Отсюда правило, которое объясняется в ответе Paercebal, согласно которому виртуальные вызовы, вызываемые из конструктора, не могут переходить в производный класс. В противном случае возможен доступ к неинициализированным членам.

person Sjoerd    schedule 29.09.2010

Если предположить, что С++ использует alloc() при выполнении нового вызова, то это может быть то, что они ищут. (С++ не знаю, так что тут могу сильно ошибаться)

Модель памяти Java выделяет кусок памяти, когда он ей нужен, и для каждого нового использует эту предварительно выделенную область. Это означает, что new в java просто устанавливает указатель на сегмент памяти и перемещает свободный указатель, в то время как new в C++ (при условии, что он использует malloc в фоновом режиме) приведет к системному вызову.

Это делает создание объектов на Java дешевле, чем на языках, использующих malloc; по крайней мере, когда не происходит инициализация.

Короче говоря, создание объектов в Java дешево — не беспокойтесь об этом, если вы не создадите их множество.

person Knubo    schedule 29.09.2010