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

c++ templates sizeof memset

3839 просмотра

7 ответа

Я написал небольшой класс координат для обработки как 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 Источник Размещён: 13.11.2019 11:42

Ответы (7)


25 плюса

Решение

Нет, не используйте 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

16 плюса

Как говорят другие, 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

5 плюса

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

sizeof(*this)

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

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

4 плюса

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

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

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

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

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

4 плюса

Диргентки это правильно. Однако вместо того, чтобы конструировать 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

3 плюса

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

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

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

1 плюс

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


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