NI>А что такое нулевое значение для класса "с виртуальной таблицей и прочими причиндалами"?
То же самое, что и для классов без причиндал, то есть, нулевое значение, заданных определением класса полей. Если конструкция {} приравнивает нулю эти поля классов без причиндал, почему это не распространяется на иные классы? Служебная инфа, вроде указателся на vtable или таблицы таковых (VTT), помещаемая компилятором в кусок памяти, занимаемую объектом, ортогональна его, заданным определением, полей.
smeeld:
NI>>А что такое нулевое значение для класса "с виртуальной таблицей и прочими причиндалами"?
S>То же самое, что и для классов без причиндал, то есть, нулевое значение, заданных определением класса полей.
Значение объекта и последовательность значений байт в его представлении — это не одно и то же.
S>Если конструкция {} приравнивает нулю эти поля классов без причиндал, почему это не распространяется на иные классы?
Во-первых, нуль нулю рознь. Даже у объекта POD типа "нулевое" значение может быть представлено ненулевыми байтами, а последовательность из нулевых байт может образовывать ненулевое значение. Например:
#include <cstring>
#include <iostream>
struct S
{
int i, j;
int S::*p;
};
#define PRINT_VALUE(expr) std::cout << #expr ": " << (expr) << std::endl;
int main()
{
S s;
s = S();
std::cout << std::boolalpha;
std::cout << "value-initialization" << std::endl;
PRINT_VALUE(s.p == nullptr);
PRINT_VALUE(s.p == &S::i);
PRINT_VALUE(s.p == &S::j);
std::memset(&s, 0, sizeof(s));
std::cout << "bytewise zeroing" << std::endl;
PRINT_VALUE(s.p == nullptr);
PRINT_VALUE(s.p == &S::i);
PRINT_VALUE(s.p == &S::j);
}
int S::* — это скалярный тип (все скалярные типы являются POD типами), и в данном случае компилятор для представления нулевого указателя на член использует последовательность значений байт, отличную от последовательности нулей.
Во-вторых, зачем подобной языковой конструкции заполнять что-то нулевыми байтами или заниматься обнулением всех полей встроенных типов, если это не обеспечивает как минимум валидность значения инициализируемого объекта? Раз ты сам взялся определять способ конструирования валидного дефолтного значения через добавление соответствующего пользовательского конструктора — тебе и карты в руки.
Здравствуйте, makdak, Вы писали:
M>Всем привет! M>можно ли так делать в членах класса? M>так то работает, сегодня..
Лучше взять в привычку делать метод очистки экземпляра класса, где аккуратно расписать обнуление всех его членов.
Потому что сегодня работает, а завтра ты так будешь делать везде
В новых плюсах очистку-инициализацию можно делать прямо в описании самого класса
class C {
int m_i = 0;
//...
}
но я всё равно обычно пишу отдельный метод Clear, по аналогии с, например std::string::clear
Здравствуйте, N. I., Вы писали:
NI>int S::* — это скалярный тип (все скалярные типы являются POD типами), и в данном случае компилятор для представления нулевого указателя на член использует последовательность значений байт, отличную от последовательности нулей.
интересный нюанс
я пытался найти в стандарте во что превращаются скалярные типы после memzero, но не смог найти =\
для каких standard-layout типов ясно определена семантика? то есть если прочитаю из того, что покрыл нулями на байтовом уровне
например, для bool верно, что false прочитаю?
Хотели удивить фактом того, что переменная типа nullptr_t не обязательно равно значению 64-ёх разрядной переменной равной нулю, или что оператор == при сравнении переменных разных типов, тем более переменных с различающимся sizeof даст неожиданные результаты? Вы почти КЭП.
D>При обнулении получишь утечку в общем случае. Вполне c-like структура.
Это если мы инициализировали void* memory, а после, не освободив, приравняли memory нолю? Не кажется Вам, что эта ситуация ортогональна теме треда, и скорее относится к тому, каким чувакам стоит не лезть в Cя, а лучше писать на яве.
uzhas:
U>я пытался найти в стандарте во что превращаются скалярные типы после memzero, но не смог найти =\
Если существует значение, которое представляется полученной последовательностью нулевых unsigned char-ов, то в объекте будет храниться это самое значение. Иначе объект, наверное, прекращает существование в соответствии с [basic.life].
U>для каких standard-layout типов ясно определена семантика?
Для unsigned char определённо
U>например, для bool верно, что false прочитаю?
Если std::memcpy-подобное копирование объекта, хранящего false, в массив unsigned char-ов образует нолики (вряд ли есть реализации, где это не так), то да. Если нолики получаются при копировании true, то будет true. Иначе формально там может получиться что угодно, насколько я вижу.
U>4 года не писал тут
Забанили за какой-то язвительный комментарий, решил отдохнуть
smeeld:
S>Хотели удивить фактом того, что переменная типа nullptr_t не обязательно равно значению 64-ёх разрядной переменной равной нулю, или что оператор == при сравнении переменных разных типов, тем более переменных с различающимся sizeof даст неожиданные результаты?
С такими кадрами, чувствую, следующие 4 года начнутся очень скоро
Здравствуйте, makdak, Вы писали:
M>>>можно ли так делать в членах класса?
J>>А зачем?
M>Чтобы обнулить всё в нём. Смотрю исходники, везде юзают: M>
M> memset(pointer_to_obj_T, 0, sizeof(T));
M>
M>вот и думаю почему ни кто не делает это в члене класса, тем более если такая операция многократно используется, над одним и тем же объектом.. M>думаю, дай поинтересуюсь, может что новое узнаю
кроме того, везде в коде, где встречается memset надо делать проверку на ловушки, которые он в себе таит, см.: https://habrahabr.ru/post/272269/
Сопровождать (модифицировать) такой код будет тяжело. Про open-closed принцип (SOLID) можно забыть.
Здравствуйте, N. I., Вы писали:
NI>С такими кадрами, чувствую, следующие 4 года начнутся очень скоро
Каков Ваш там выше пример, таков и ответ. Конструкции T::x и T::*x в С++ имеют особое значение и нечего за них цепляться. Значение &T::x вообще равно смещению поля в пределах объекта в байтах, а не значению виртуального адреса поля объекта.
Если же прогнать такой код
include <cstring>
#include <iostream>
struct S
{
int i, j;
int *p;
};
#define PRINT_VALUE(expr) std::cout << #expr ": " << (expr) << std::endl;
int main()
{
S s = S();
std::cout << std::boolalpha;
PRINT_VALUE(s.p == 0);
PRINT_VALUE(s.p == &s.i);
PRINT_VALUE(s.p == &s.j);
std::memset(&s, 0, sizeof(s));
std::cout << "bytewise zeroing" << std::endl;
PRINT_VALUE(s.p == 0);
PRINT_VALUE(s.p == &s.i);
PRINT_VALUE(s.p == &s.j);
}
Здравствуйте, smeeld, Вы писали:
S>...Конструкции T::x и T::*x в С++ имеют особое значение и нечего за них цепляться. Значение &T::x вообще равно смещению поля в пределах объекта в байтах, а не значению виртуального адреса поля объекта.
... S>Что доказывает, что memset даёт те же зачения полям, что и другие способы обнуления.
есть много примеров сайт эффектов memset. Да, он может "давать те же значения полям, что и другие способы обнуления". Но я пока не увидел его достоинств по сравнению с "классическим" (стандартным) методом инициализации структур.
Здравствуйте, smeeld, Вы писали:
NB>>чисто для справки, почему s.p сравнивается именно с &s.i и &s.j? NB>>здесь скрыт какой-то сакральный смысл?
S>Чисто для прикола и демонстрации отличия &s.i от &S::i
ну для демонстрации логично было бы и &S::i где-нибудь использовать, не?
Тут в одном треде LapteVV приводил ссылки по полезным C++-ым ресурсам, пробежался по ним, всковырнул сочиненьице одного из гуру, а там нашёл такой код.
Предлагаю уважаемой публике оценить этот код, там как раз этот самый memset преступно вовсю юзается, там ещё и memcpy, это вообще преступление.
#define _CRT_SECURE_NO_WARNINGS // Avoid warning for vsnprintf function in MS compiler#include <memory.h> // For memcpy and memset#include <string.h> // For strlen, strcmp, strchr#include <stdlib.h> // For exit in Error function#include <stdarg.h> // For va_list, va_start#include <stdio.h> // Needed for example only
//#include <varargs.h> // Include varargs.h for va_list, va_start only if required by your system#ifdef USE_ASMLIB
#include"asmlib.h"// Header file for asmlib library#define memcpy_ A_memcpy // Use asmlib versions of string functions#define memset_ A_memset
#define strlen_ A_strlen
#define strcmp_ A_strcmp
#define strstr_ A_strstr
#define MEM_PADDING 15 // Padding of memory buffer#else
#define memcpy_ memcpy // Use standard library versions of string functions#define memset_ memset
#define strlen_ strlen
#define strcmp_ strcmp
#define strstr_ strstr
#define MEM_PADDING 0
#endif// Define pointer to zero-terminated stringtypedef char const * PChar;
// Class definition for StringPoolSclass StringPoolS {
public:
// Helper class StringElement. This class defines all the operators and
// functions we can apply to StringPoolSObject[i]:class StringElement {
public:
// Constructor:
StringElement(StringPoolS & a, int i) : arr(a) {index = i;};
// Get string length:int Len() {return strlen_(arr.Get(index));};
// Format string using printf style formating:
StringElement const & Printf(const char * format, ...);
// Write string to file or stdout:int Write(FILE * f);
// Search in string:int SearchForSubstring(PChar s);
// Assign substring:
StringElement const & SetToSubstring(PChar s, int start, int len);
// Operator '=' assigns string:
PChar operator = (PChar s) {arr.Set(index, s); return *this;};
PChar operator = (StringElement const & s) {return *this = PChar(s);};
// Operator '+=' concatenates strings:void operator += (const char * s) {arr.Concatenate(index, s);};
// Automatic conversion to const char *operator PChar() const {return arr.Get(index);};
// Operator '[]' gives access to a single character in a string:inline char & operator[] (int i);
protected:
StringPoolS & arr; // Reference to the StringPoolS that created this objectint index; // Index to string in StringPoolS
};
friend class StringElement; // Friend class defining all operations we can do on StringPoolObject[i]
// Members of class StringPoolS
StringPoolS(); // Constructor
~StringPoolS(); // Destructorvoid Clear(); // Erase all stringsint GetNum() const {return Num;} // Get number of strings
StringElement operator[] (int i) { // Operator '[]' gives access to a string in StringPoolS return StringElement(*this, i);}
void Set(int i, PChar s); // Insert zero-terminated string. Used by StringElement void Set(int i, PChar s, int Len); // Insert non zero-terminated string. Used by StringElement void Concatenate(int i, PChar s); // Concatenate strings. Used by StringElement void ReserveBuf(int newsize); // Allocate memory for string spacevoid ReserveNum(int newsize); // Allocate memory for string indices
// Define desired allocation sizes. You may change these values:enum DefineSizes {
AllocateSpace1 = 4096, // Minimum number of characters to allocate in string buffer
AllocateSpace2 = 1024, // Minimum number of indices to allocate in offsets buffer
FormatLength = 1023, // Maximum length of strings written with Printf
MemPadding = MEM_PADDING // Memory buffer padding
};
protected:
char * Get(int i) const; // Read string. Used only from StringElement void Error(int message,int i) const;// Produce fatal error message.private:
char * Buffer; // Memory block containing stringschar * OldBuffer; // Save old Buffer during copying of stringunsigned int * Offsets; // Memory block containing offsets to stringsint BufferSize; // Size of buffer blockint OffsetsSize; // Size of Offsets blockint DataSize; // Used part of Buffer, including garbageint GarbageSize; // Total size of garbage in Bufferint Top; // Highest used Offsetint Num; // Number of strings = highest used index in Offsets + 1char * Allocate(int i, int len); // Make space for a new string
StringPoolS(StringPoolS &){}; // Private copy constructor to prevent copyingvoid operator = (StringPoolS &){}; // Private operator = to prevent copying
};
inline bool operator == (StringPoolS::StringElement const & a, StringPoolS::StringElement const & b) {
return strcmp_(a, b) == 0;
}
inline bool operator != (StringPoolS::StringElement const & a, StringPoolS::StringElement const & b) {
return strcmp_(a, b) != 0;
}
inline bool operator < (StringPoolS::StringElement const & a, StringPoolS::StringElement const & b) {
return strcmp_(a, b) < 0;
}
inline bool operator > (StringPoolS::StringElement const & a, StringPoolS::StringElement const & b) {
return strcmp_(a, b) > 0;
}
/******************************************************************************
2. Function definition part. Put this in a .cpp file:
******************************************************************************/
// Default constructor
StringPoolS::StringPoolS() {
// Set everything to zero
memset_(this, 0, sizeof(*this));
}
// Destructor
StringPoolS::~StringPoolS() {
// Free allocated memoryif (Buffer) delete[] Buffer;
if (OldBuffer) delete[] OldBuffer;
if (Offsets) delete[] Offsets;
// Set everything to zero
memset_(this, 0, sizeof(*this));
}
// Erase all stringsvoid StringPoolS::Clear() {
// Set all offsets to zeroif (Offsets) memset_(Offsets, 0, Num * sizeof(*Offsets));
// Indicate that Buffer is empty, but do not deallocate
DataSize = GarbageSize = 0;
}
// Insert string. Used by StringElement void StringPoolS::Set(int i, PChar s) {
if (i < 0) Error(1, i);
if (i >= Num) {
// i is higher than any previous index
Num = i + 1;
if (i >= OffsetsSize) {
// Make Offsets buffer bigger
ReserveNum(i + 1);
}
}
// Length of new stringint Len = 0;
if (s) Len = strlen_(s);
if (Len == 0) {
// Erase stringif (Offsets[i]) {
GarbageSize += strlen_(Buffer + Offsets[i]) + 1;
Offsets[i] = 0;
}
return;
}
// Make space for stringchar * p = Allocate(i, Len);
// Insert string
memcpy_(p, s, Len+1);
// Release OldBuffer if anyif (OldBuffer) {
delete[] OldBuffer; OldBuffer = 0;
}
}
// Insert non zero-terminated string. Used by StringElementvoid StringPoolS::Set(int i, PChar s, int Len) {
if (i < 0) Error(1, i);
if (i >= Num) {
// i is higher than any previous index
Num = i + 1;
if (i >= OffsetsSize) {
// Make Offsets buffer bigger
ReserveNum(i + 1);
}
}
// Length of new stringif (Len <= 0) {
// Erase stringif (Offsets[i]) {
GarbageSize += strlen_(Buffer + Offsets[i]) + 1;
Offsets[i] = 0;
}
return;
}
// Make space for stringchar * p = Allocate(i, Len);
// Insert string
memcpy_(p, s, Len);
// Zero-terminate
p[Len] = 0;
// Release OldBuffer if anyif (OldBuffer) {
delete[] OldBuffer; OldBuffer = 0;
}
}
Ops>Если говорить про ключевые слова, то да. А если логически разделять, не привязываясь к языку?
Ну это такой вопрос, который задаётся в контексте языка. В C# понятно — там это в первую очередь разделение по типу памяти. Но в контексте задачи про memset это явно про C++.
Здравствуйте, SaZ, Вы писали:
SaZ>Ну это такой вопрос, который задаётся в контексте языка. В C# понятно — там это в первую очередь разделение по типу памяти. Но в контексте задачи про memset это явно про C++.
Подозреваю, в контексте ответа, имелись в виду C-like структуры. Чуятельный орган подсказывает.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.