2011-01-02 5 views
1

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

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

У меня есть две части к этому вопросу.

  1. Есть ли причина, по которой эта идея не будет работать на практике на любом производственном компиляторе?

  2. Это технически нарушает спецификацию языка C++?

+0

Вы имеете в виду собственно называть деструктора себя в коде? Вы должны (почти) НИКОГДА не делать этого. – Falmarri

+3

Размещение новое является одним из тех редких случаев, когда вы должны. –

+2

@ Джош: Я не думаю, что понимаю проблему. Почему бы не создать объект C как единственный элемент данных вашего объекта C++? Затем в ваших функциях-членах передайте указатель на этот член в функцию API C, которую вы делегируете. –

ответ

2

Если я правильно понимаю ваш вопрос у вас есть объект C в памяти, и вы хотите, чтобы инициализировать C++ объекта с тот же макет «поверх» существующего объекта.

CppObject* cppobject = new (cobject) CppObject; 

Хотя нет никаких проблем с не вызывая деструктор для старого объекта - вызывает ли это утечки ресурсов или другие вопросы, полностью вниз к типу старого объекта и является проблемой коды пользователя, а не язык проблема совместимости - тот факт, что вы повторно используете память для нового объекта, означает, что старый объект больше не доступен.

Хотя форма размещения operator new должна просто вернуть адрес, который она была предоставлена, нет ничего, чтобы остановить само новое выражение, которое протирает память для нового объекта до вызова любого конструктора (если таковой имеется). Члены нового объекта, которые не инициализируются в соответствии с правилами языка C++, имеют неопределенное содержимое, которое определенно не означает то же, что и содержимое любого старого объекта, который когда-то жил в повторно используемой памяти.

Если я правильно вас понимаю, то, что вы пытаетесь сделать, не гарантируется.

+0

Стандарт останавливает размещение нового от вытирания памяти перед ctor: «Намеренно не выполняет никаких других действий». [§18.4.1.3p3, C++ 03] Если я не понимаю это. –

+0

@Fred Nurk: Нет, это требование для формы размещения ':: operator new' и может быть проверено с явным вызовом' :: operator new'. Новое выражение инициализирует новый объект; нет ничего, что помешало бы компилятору вставить магию после вызова соответствующего «оператора new» и до того, как объект будет инициализирован для сброса памяти или когда части объекта остаются «неинициализированными», заполняя эти части ненужными значениями. –

0

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

Есть ли причина, по которой эта идея не будет работать на практике на любом производственном компиляторе?

Вы чертовски хорошо знаете, что ваш объект C++ соответствует размеру объекта C.

Это технически нарушает спецификацию языка C++?

Нет, но не все, что относится к спецификации, будет работать так, как вы хотите.

+1

Зачем нужен деструктор? –

+1

@Charles: Даже если конструктор ничего не делает сейчас. Вы открываете себя для целого ряда проблем, если кто-то другой меняет структуру, чтобы включить члена, которому требуется его деструктор (т.е. делает его не POD).Код, который вы пишете сейчас, не должен полагаться на артефакт текущей реализации, который может не соответствовать будущим версиям программного обеспечения (особенно, если код распространяется в более чем одном месте). –

+2

@ Мартин Йорк: Я не отрицаю, что это вообще «плохая идея», я просто подсказывал Ною Робертсу, чтобы выяснить, требуется ли это строго или просто плохая практика. –

2

Я думаю, что ответ заключается в том, что если ваш класс POD (что это такое, если это правда, что он ничего не делает в con/destructor, не имеет виртуальных функций-членов и не имеет нестатических элементов данных с любым из эти вещи), тогда вам не нужно вызывать конструктор или деструктором, его время жизни - это всего лишь время жизни базовой памяти. Вы можете использовать его так же, как структура используется в C, и вы можете вызывать ее функции-члены независимо от того, была ли она построена.

+0

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

+1

@Charles: Я так думаю, я пытался указать, что в дополнение к тому, что хочет этот вопрос, если его класс POD, он тоже не нуждается в размещении. –

+0

Если он не хочет использовать конструктор не по умолчанию для инициализации объекта, что очень удобно. –

1

Цель размещения new - позволить создавать пулы объектов или совмещать несколько объектов вместе в непрерывном пространстве памяти, как и для std :: vector.

Если объекты являются C-структурами, для этого вам не требуется новое место размещения, вы можете просто использовать метод C выделения памяти на основе sizeof (struct Foo), где Foo - это имя структуры, и если вы выделите несколько объектов, которые вам могут понадобиться, чтобы увеличить размер до границы для выравнивания.

Однако нет необходимости размещение новых объектов там, вы можете просто тетср их.

0

Я понимаю, что размещение новых вызовов обычно сопровождается явным вызовом деструктора. Если мне не нужно деструктор (нет кода для ввода там и нет переменных-членов, у которых есть деструкторы), могу ли я безопасно пропустить явный вызов деструктора?

Да. Если мне не нужно лететь в Нью-Йорк, прежде чем отправлять этот ответ, могу ли я спокойно пропустить поездку? :) Однако, если деструктор действительно не нужен, потому что он ничего не делает, то какой вред там при вызове?

Если компилятор может определить, что деструктор должен быть не-op, я бы ожидал, что он устранит этот вызов. Если вы не пишете явный dtor, помните, что ваш класс все еще имеет dtor, и интересный случай - здесь - это то, что язык вызывает тривиальным.

Решение: уничтожить ранее построенные объекты, прежде чем строить над ними, даже если вы считаете, что dtor не является оператором.

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

Это назначение классов, совместимых с макетами, и reinterpret_cast. Включите статическое утверждение (например, макрос Boost, 0x static_assert и т. Д.), Проверяя размер и/или выравнивание, как вы пожелаете, для короткой проверки работоспособности, но в конечном итоге вам нужно немного узнать, как ваша реализация определяет классы. Большинство из них обеспечивают прагмы (или другие механизмы, связанные с реализацией), чтобы контролировать это, если это необходимо.

Самый простой способ, чтобы содержать C-структуру в пределах типа C++:

// in C header 
typedef struct { 
    int n; 
    //... 
} C_A; 
C_A* C_get_a(); 

// your C++ 
struct A { 
    void blah(int n) { 
    _data.num += n; 
    } 

    // convenience functions 
    static A* from(C_A *p) { 
    return reinterpret_cast<A*>(p); 
    } 
    static A const* from(C_A const *p) { 
    return reinterpret_cast<A const*>(p); 
    } 

private: 
    C_A _data; // the only data member 
}; 

void example() { 
    A *p = A::from(C_get_a()); 
    p->blah(42); 
} 

Я хотел бы сохранить такие преобразования инкапсулируются, а не посыпая reinterpret_casts повсюду, и многое другое равномерное (т. е. сравнение сайта вызова для const и не const), следовательно, функции удобства. Также немного сложнее изменить класс, не заметив, что этот тип использования все равно должен поддерживаться.

В зависимости от конкретного класса вы можете сделать этот элемент данных общедоступным.

0

Первый вопрос: почему вы просто не используете актерский состав? Тогда нет никаких проблем с размещением нового, что-либо делает, и, очевидно, нет проблемы с отказом от использования деструктора. Результат будет работать, если типы C и C++ совместимы с макетом.

Второй вопрос: в чем смысл? Если у вас нет виртуальных функций, вы не используете конструктор или деструктор, класс C++, похоже, не имеет каких-либо преимуществ перед использованием только типа C: любые методы, которые вы пишете, должны были быть глобальными функциями.

Единственное преимущество, которое я могу себе представить, - это если вы хотите скрыть представление C, вы можете наложить объект C классом со всеми частными членами и использовать методы для доступа. Это ваша цель? [Это разумная вещь, я думаю]