C 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 ++ является законным, безопасным и разумным , это не относится к их числу.
5 плюса
Проблема в том, что это тип Pointer, который составляет 4 байта (в 32-битных системах), а int - 4 байта (в 32-битных системах). Пытаться:
sizeof(*this)
Изменить: Хотя я согласен с другими, что списки инициализатора в конструкторе, вероятно, является правильным решением здесь.
Автор: i_am_jorf Размещён: 12.04.2009 08:584 плюса
Не используйте memset. Это ужасно сломается на не POD-типах (и не обязательно будет легко отлаживаться), и в этом случае, вероятно, будет намного медленнее, чем простая инициализация обоих членов нулем (два назначения против вызова функции).
Более того, вы обычно не хотите обнулять всех членов класса. Вы хотите обнулить те, для которых ноль является значимым значением по умолчанию. И вы должны привыкнуть инициализировать своих членов к значимой ценности в любом случае. Общее обнуление всего и притворство, что проблемы не существует, просто гарантирует много головных болей позже. Если вы добавляете члена в класс, решите, должен ли этот член инициализироваться и как.
Если и когда вам нужна функциональность, подобная memset, используйте хотя бы std :: fill, который совместим с не POD-типами.
Если вы программируете на C ++, используйте инструменты, доступные в C ++. В противном случае назовите это C.
Автор: jalf Размещён: 12.04.2009 09:154 плюса
Диргентки это правильно. Однако вместо того, чтобы конструировать 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:081 плюс
Это может работать вместо:
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
Вопросы из категории :
- c++ What are the barriers to understanding pointers and what can be done to overcome them?
- c++ Какой самый простой способ для анализа файла INI в C ++?
- c++ Когда вы должны использовать «друг» в C ++?
- c++ Как вы очищаете переменную stringstream?
- c++ В C ++ конструктор и деструктор могут быть встроенными функциями?
- templates Изменить шаблоны в Xcode
- templates Каковы различия между «универсальными» типами в C ++ и Java?
- templates Как добавить отражение в приложение C ++?
- templates Проверьте, имеет ли класс функцию-член с заданной сигнатурой
- templates Есть ли способ отладки шаблонов Velocity в традиционном смысле отладки кода?
- sizeof Why isn't sizeof for a struct equal to the sum of sizeof of each member?
- sizeof Is the sizeof(some pointer) always equal to four?
- sizeof Как определить размер объекта в Python?
- sizeof Как найти «sizeof» (указатель на массив)?
- sizeof sizeof (int) на x64?
- memset C memset, кажется, не пишет каждому члену
- memset Что эквивалентно memset в C #?
- memset Как memset () памяти для определенного шаблона вместо одного байта?
- memset Почему memset принимает int вместо char?
- memset Почему memset () неправильно инициализирует int?