Как сделать эквивалент memset (это, ...), не заглушая vtbl?

Я знаю, что memset не одобряется дляclass инициализация. Например, что-то вроде следующего:

class X { public: 
X() { memset( this, 0, sizeof(*this) ) ; }
...
} ;

забьетvtbl если там'сvirtual функция в миксе.

Я работаю над (огромной) унаследованной кодовой базой, которая написана на языке C, но скомпилирована в C ++, поэтому все рассматриваемые члены обычно являются POD и не требуют традиционных конструкторов C ++. Использование C ++ постепенно распространяется (как виртуальные функции), и это кусает разработчиков, которые неЯ не понимаю, что у memset есть эти дополнительные зубы C ++.

Интересно, существует ли безопасный для C ++ способ начальной инициализации с нулевым перехватом, который может сопровождаться определенной инициализацией по элементам, где нулевая инициализация не является 'не подходит?

Я нахожу похожие вопросыmemset для инициализации в C ++, а такжеобнуление производной структуры с использованием memset, У обоих естьдон»не использовать memset () " ответы, но нет хороших альтернатив (особенно для больших структур, потенциально содержащих много членов).

 WeirdlyCheezy15 окт. 2012 г., 21:59
@ Hot Licks What You 'Предлагаю работает нормально, если вытолько на одном компиляторе и осторожно используйте этот компиляторПравила расположения памяти. Например, если ваш проект только когда-либо компилируется с MSVC, это может работать. Но разные компиляторы имеют разные правила для vtables и т. Д. (Теоретически они могут даже использовать что-то, кроме vtable). Таким образом, это может легко превратиться в беспорядок страшных if-def, если вам нужно использовать этот вид трюка на нескольких компиляторах.
 Keith Thompson15 окт. 2012 г., 21:37
Дон»ты имеешь в видуmemset(this, 0, sizeof *this)? (И да, это также затормозило бы vtbl.)
 Hot Licks15 окт. 2012 г., 21:36
Другой вариант - написать собственную подпрограмму alloc для класса, чтобы вы могли убедиться, что пространство очищено, но у IIRC есть ограничения вокруг этого.
 David Rodríguez - dribeas15 окт. 2012 г., 22:21
Какие'неправильно с написаниемконструктор что явно инициализирует членов?
 AShelly15 окт. 2012 г., 21:37
@ HotLicks, похоже, что проблема с этим подходом заключается в знании правильного размера. sizeof (это) выигралвероятно, будет таким же, как sizeof (<части данных>).
 GManNickG15 окт. 2012 г., 22:29
Это н'Что касается vtable, вы также можете иметь нестандартные элементы макета, которыеmemset будет также испортить Сделайте выбор: пишите на C (и разбирайте части C ++) или пишите на C ++ (и используйте идиомы C ++). Смешивать эти две вещи опасно, и этоВероятно, легче (и лучше) начать тратить время на исправление этих классов по одному, чтобы дать им надлежащие конструкторы, без трюков на Си.
 Hot Licks15 окт. 2012 г., 21:35
Обычно вы берете адрес первого поля и очищаете оттуда. Без сомнения, это нарушает какое-то правило, но этоУ меня всегда получалось.
 Hot Licks15 окт. 2012 г., 21:39
Вы можете взять адрес поля и вычестьthis из этого, затем вычтите остаток от размера объекта.
 Hot Licks16 окт. 2012 г., 22:33
@WeirdlyCheezy - Как я уже сказал, это, несомненно, нарушает несколько правил, но этоУ меня всегда получалось.

Ответы на вопрос(7)

Это отвратительно, но тымог перегрузкаoperator new/delete для этих объектов (или в общем базовом классе) и имеют реализацию, обеспечивающую ноль 'из буфера. Что-то вроде этого :

class HideousBaseClass
{
public:
    void* operator new( size_,t nSize )
    {
        void* p = malloc( nSize );
        memset( p, 0, nSize );
        return p;
    }
    void operator delete( void* p )
    {
        if( p )
            free( p );
    }
};

Можно также переопределитьГлобальный new/delete операторы, но это может иметь негативные последствия перфект.

Редактировать: Я только что понял, что этот подход выигралработать со стековыми объектами.

 AShelly15 окт. 2012 г., 21:41
тот'неполностью отвратительное ...

чтобы они, так сказать, очищались.

 Peeter Joot17 окт. 2012 г., 22:01
Вопрос действительно в том, что положить в конструктор (это нет, скажем, 100 операторов присваивания).

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

Эта структура не имеет никакой жизнеспособности и не расширяет ничего. Это будет просто кусок данных. Таким образом, memsetting структуры безопасен.

Я сделал пример:

#include <iostream>
#include <cstring>

struct X_c_stuff {
    X_c_stuff() {
        memset(this,0,sizeof(this));
    }
    int cMember;
};
class X : private X_c_stuff{
public:
    X() 
    : normalMember(3)
    {
        std::cout << cMember << normalMember << std::endl;
    }
private:
    int normalMember;
};

int main() {
    X a;
    return 0;
}
</cstring></iostream>

чтобы найти диапазон байтов, который вы хотите обнулить:

class Thing {
public:
    Thing() {
        memset(&data1, 0, (char*)&lastdata - (char*)&data1 + sizeof(lastdata));
    }
private:
    int data1;
    int data2;
    int data3;
    // ...
    int lastdata;
};

(Изменить: я изначально использовалoffsetof() для этого, но комментарий указал, что это должно работать только на POD, и тогда я понял, что вы можете просто использовать адреса членов напрямую.)

 K-ballo15 окт. 2012 г., 22:08
offsetofгарантированно работать только наPOD типы

Попробуй это:

template <class t="">
void reset(T& t)
{
   t = T();
}
</class>

Это обнулит ваш объект - неважно, POD или нет.

Но не делайте этого:

   A::A() { reset(*this); }

Это вызоветA::A в бесконечной рекурсии !!!

Попробуй это:

  struct AData { ... all A members };
  class  A { 
   public: 
      A() { reset(data); } 
   private: 
      AData data; 
   };
 Peeter Joot27 апр. 2013 г., 16:06
Это то, что повышение value_initialized:boost.org/doc/libs/1_38_0/libs/utility/value_init.htm  делается?
 PiotrNycz28 апр. 2013 г., 14:23
@PeeterJoot Более или менее это делает. На самом деле она делает это более сложным способом. Эта библиотека поддержки утверждает, что из-за некоторых проблем с компилятором и потому что способ, представленный в моем ответе, не всегда является наиболее эффективным способом для не POD-типов, она использует статическую константу для этой цели. Однако, если вы ищете замену memset - мой "упрощенный» пути должно быть достаточно.
 PiotrNycz05 июн. 2017 г., 22:33
@Shahbaz - обнуляет объект в смысле c ++: для типов POD - все числа в нули, bool в false, указатели в null. Для не POD (случай, который вы описали) будет использоваться созданный по умолчанию объект - так что если кто-то напишет конструктор по умолчанию неправильно - тогда нет - он потерпит неудачу. Но посмотрите на приведенный мною пример - AData определяется как простой агрегат, поэтому, если его членами являются POD или не POD с допустимым конструктором по умолчанию - он всегда будет работать.
 Shahbaz02 июн. 2017 г., 16:35
t = T(); это победилоЕсли объект обнулять, он заменит его конструкцией по умолчанию. Если конструктор по умолчанию неt инициализировать член, этот член все равно останется не инициализированным. Если оно инициализирует что-то ненулевое, оно также останется с этим значением. Сброс кдефолт вместовсе нули на самом деле может быть хорошей вещью, но они не такие, как вы предлагаете.

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

редактировать: На самом деле, это не должноигнорировать указатель, его следует сравнить сthis, В случае соответствия сделайте правильную вещь для объекта, в случае несоответствия перенаправьте в глобальную функцию.

 Hot Licks15 окт. 2012 г., 22:36
Да, тамВ этом есть некоторая заслуга. Проще запомнить, чтобы он был обновленным, чем управлять списками в 5 разных конструкторах.
 Pete Becker15 окт. 2012 г., 21:57
+1 за ум

что статический экземпляр инициализируется нулем:https://ideone.com/GEFKG0

template <class t="">
struct clearable
{
    void clear()
    {
        static T _clear;
        *((T*)this) = _clear;
    };
};

class test : public clearable<test>
{
    public:
        int a;
};

int main()
{
    test _test;
    _test.a=3;
    _test.clear();

    printf("%d", _test.a);

    return 0;
}
</test></class>

Однако приведенное выше вызовет повторный вызов конструктора (шаблонного класса).

Для решения, которое не вызывает вызов ctor, это можно использовать вместо этого:https://ideone.com/qTO6ka

template <class t="">
struct clearable
{
    void *cleared;
    clearable():cleared(calloc(sizeof(T), 1)) {}

    void clear()
    {
        *((T*)this) = *((T*)cleared);
    };
};
</class>

...и если вы'При использовании C ++ 11 и далее можно использовать следующее:https://ideone.com/S1ae8G

template <class t="">
struct clearable
{
    void clear()
    {
        *((T*)this) = {};
    };
};
</class>

Ваш ответ на вопрос