Вопрос:

C memset, кажется, не пишет каждому члену

c++ templates sizeof memset

3839 просмотра

7 ответа

24800 Репутация автора

Я написал небольшой класс координат для обработки как int, так и float координат.

template <class T>
class vector2
{
public:
    vector2() { memset(this, 0, sizeof(this)); }
    T x;
    T y;
};

Затем в main () я делаю:

vector2<int> v;

Но согласно моему отладчику MSVC, только значение x установлено на 0, значение y остается неизменным. Я никогда раньше не использовал sizeof () в шаблонном классе, может ли это быть причиной проблемы?

Автор: Mizipzor Источник Размещён: 12.04.2009 08:56

Ответы (7)


5 плюса

45978 Репутация автора

Проблема в том, что это тип Pointer, который составляет 4 байта (в 32-битных системах), а int - 4 байта (в 32-битных системах). Пытаться:

sizeof(*this)

Изменить: Хотя я согласен с другими, что списки инициализатора в конструкторе, вероятно, является правильным решением здесь.

Автор: i_am_jorf Размещён: 12.04.2009 08:58

25 плюса

94092 Репутация автора

Решение

Нет, не используйте memset- он обнуляет размер указателя (4 байта на моей машине Intel x86), начиная с места, на которое указывает указатель this. Это плохая привычка: вы также обнуляете виртуальные указатели и указатели на виртуальные базы при использовании memsetсо сложным классом. Вместо этого сделайте:

template <class T>
class vector2
{
public:
    // use initializer lists
    vector2() : x(0), y(0) {}
    T x;
    T y;
};
Автор: dirkgently Размещён: 12.04.2009 08:59

3 плюса

17556 Репутация автора

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

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

Автор: lothar Размещён: 12.04.2009 09:08

16 плюса

35602 Репутация автора

Как говорят другие, memset()это не правильный способ сделать это. Однако есть некоторые тонкости, почему нет.

Во-первых, вы пытаетесь использовать memset()только очистку sizeof(void *)байтов. Для вашего примера, это, очевидно, совпадение байтов, занятых xэлементом.

Простым исправлением было бы написать memset(this, 0, sizeof(*this)), который в этом случае установил бы xи y.

Однако, если у вашего vector2класса есть какие-либо виртуальные методы и обычный механизм используется для их представления вашим компилятором, то это memsetуничтожит vtableи сломает экземпляр, установив vtableуказатель на NULL. Что плохо

Другая проблема состоит в том, что если тип Tтребует какого-либо более сложного действия конструктора, чем просто установка его битов в 0, тогда конструкторы для членов не вызываются, но их эффект разрушается перезаписью содержимого членов memset().

Единственное правильное действие - написать конструктор по умолчанию как

vector2(): x(0), y(0), {}

и просто забыть о попытке использовать memset()для этого вообще.

Редактировать: Д. Шоули указал в комментарии, что конструкторы по умолчанию для xи yбыли фактически вызваны перед memset()в исходном коде, как представлено. Хотя это технически верно, колл memset()перезаписывает участников, что в лучшем случае очень, очень плохо, а в худшем вызывает демонов Неопределенного Поведения.

Как написано, vector2классом является POD, при условии, что тип Tтакже представляет собой простые старые данные, как было бы, если бы Tбыли intили float.

Однако все, что нужно для того, Tчтобы стать своего рода bignumклассом значений, вызвать проблемы, которые действительно трудно диагностировать. Если вам повезет, они начнут проявляться на ранних этапах через нарушения прав доступа из-за разыменования указателей NULL, созданных memset(). Но Lady Luck - непостоянная любовница, и более вероятным результатом будет утечка памяти, и приложение становится «шатким». Или, скорее, «потряснее».

ОП спросил в комментарии к другому ответу: «... Есть ли способ заставить memset работать?»

Ответ там просто: «Нет».

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

Автор: RBerteig Размещён: 12.04.2009 09:09

4 плюса

214694 Репутация автора

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

Более того, вы обычно не хотите обнулять всех членов класса. Вы хотите обнулить те, для которых ноль является значимым значением по умолчанию. И вы должны привыкнуть инициализировать своих членов к значимой ценности в любом случае. Общее обнуление всего и притворство, что проблемы не существует, просто гарантирует много головных болей позже. Если вы добавляете члена в класс, решите, должен ли этот член инициализироваться и как.

Если и когда вам нужна функциональность, подобная memset, используйте хотя бы std :: fill, который совместим с не POD-типами.

Если вы программируете на C ++, используйте инструменты, доступные в C ++. В противном случае назовите это C.

Автор: jalf Размещён: 12.04.2009 09:15

4 плюса

3018 Репутация автора

Диргентки это правильно. Однако вместо того, чтобы конструировать x и y с 0, явный вызов конструктора по умолчанию установит для внутренних типов 0 и позволит использовать шаблон для структур и классов с конструктором по умолчанию.

template <class T>
class vector2
{
public:
    // use initializer lists
    vector2() : x(), y() {}
    T x;
    T y;
};
Автор: Stephen Nutt Размещён: 13.04.2009 01:07

1 плюс

99 Репутация автора

Это может работать вместо:


char buffer[sizeof(vector2)];
memset(buffer, 0, sizeof(buffer));
vector2 *v2 = new (buffer) vector2();

... или заменить / переопределить vector2 :: new, чтобы сделать что-то подобное. Все еще кажется странным для меня, хотя.

Определенно идти с


vector2(): x(0), y(0), {}

Автор: Martin Размещён: 18.04.2009 06:24
Вопросы из категории :
32x32