2013-06-26 6 views
1

Построение объектов в куче - обманчиво простая задача на языке C++. Когда кодМожет ли новый оператор вернуть до завершения строительства?

T* object = new T(); 
func(object); 

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

Мой вопрос: Может ли operator new вернуть указатель на выделенную/инициализированную память до того, как объект будет построен, так что func(...) работает на не полностью построенном объекте? Ответ на этот вопрос имеет значение для многопоточной библиотеки, которую я разрабатываю.

Заранее спасибо.

+2

'operator new' * должен * вернуть указатель перед тем, как объект * начнется *. Где он будет построен без указателя? –

+0

Конечно, но я имел в виду нечто похожее на «вернуть указатель и продолжить выполнение в текущей области». Я отредактировал исходное сообщение, чтобы немного разъяснить свой смысл. –

+0

Вы задаете неправильный вопрос. Вам нужно узнать порядок памяти. –

ответ

6

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

+0

Предположим, что это вызов функции 'func (object)' который * делает * объект видимым для других потоков. В этом случае, возможно ли, что будут проблемы с потоками? –

+0

@ DanM.Katz - если функция делает объект видимым при правильной синхронизации, тогда он будет работать нормально. Если нет, то вы не можете положиться на это. Никакое количество разговоров вокруг не удалит требование синхронизации. –

+0

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

1

object не укажет на объект до его постройки. new T() не вернется до его завершения.

Я думаю, что Пит Бекер правильно с проблемой кэш, хотя ...

0

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

Я не знаю среднего шага. Для вашей формы operator new вызывается тогда конструктор на память, возвращенную первым.

Кроме того, виртуальная таблица объекта не обязательно доступна до завершения строительства.

Он доступен - в некотором смысле (стандарт не предусматривает VMT вообще, просто поведение), простыми словами во время строительства класс ведет себя так, как если бы это был «самый производный» для активного ctor. Виртуальные вызовы могут быть выданы, но они приземляются на Base :: foo из Base :: Base(), и если вызывается из списка инициализации, необходимо принять меры к тому, что некоторые части еще не построены.

Мой вопрос: может ли оператор new возвращать указатель на выделенную/инициализированную память до того, как объект будет построен?

operator newвсегда работает и заканчивается перед началом т е р.

Но вы могли бы означать new operator, как в своем вопросе. Это возвращается только тогда, когда все сделано, ctor выполнил свою работу.

1

Я думаю, что есть замешательство между operator new() и T* object = new T();, производя значение object.

Если мы посмотрим на это очень simplisticly (и это только для целей иллюстрации, не имел в виду, чтобы быть точным определением того, как компилятор C++ на самом деле делает вещи), мы можем разбить T* object = new T(); на пару частей:

void *temp = ::operator new(sizeof(T)); // Now we have SPACE for a T object. 
(T*)temp->T();  // Construct the contents of T. 
object = (T *)temp; // assign `object`. 

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

Это происходит в этом порядке, и если ваш конструктор не запускает поток, который выполняет некоторую работу, которая «завершает конструкцию», вы не можете иметь значение в object, которое не полностью сконструировано.

+0

Ваш 'Construct содержание T' синтаксиса i странный и не будет работать.Синтаксис C++ для прямого вызова конструктора - это размещение - новое. Конечно, я понимаю, что ваш код должен быть только иллюстративным, но почему бы не использовать существующий синтаксис? –

+0

Лучше сейчас? - Я изменил «аргумент» на «вызов метода броска». –

+0

Это работает? (Edit: no.) Почему бы просто не объединить последние две строки в 'T * object = new (temp) T();'? –