Наиболее элегантное обнуление членов класса при инициализации
Что нам позволяет синтаксис C++:
Вариант 1:
{
public:
int var1;
float var2;
char var3;
test()
{
var1=0;
var2=0;
var3=0;
}
~test(){}
};
Вариант 2:
{
public:
int var1;
float var2;
char var3;
test(): var1(0), var2(0), var3(0){}
~test(){}
};
Вариант 3:
{
public:
int var1;
float var2;
char var3;
test(){ memset(this, NULL, sizeof(*this); }
~test(){}
};
В примерах приведено только по три члена класса, что в данном случае позволяет легко проинициализировать каждый из них вручную. А если параметров 100? Казалось бы тут может выручить третий вариант, который берет и забивает всю выделенную под класс область памяти нулями. Но третий вариант не прокатывает, если члены класса представляют собой другие классы в особенности испльзующие указатели.
К примеру, в данном случае:
{
public:
string var;
test(){ memset(this, NULL, sizeof(*this); }
~test(){}
};
гробится член var типа string.
Как выход, можно создавать такие члены класса динамически, после обнуления, но это опять возврат к первому варианту.
Вот и возник вопрос, как автоматизировать, чтоб не ручками каждую переменную обнулять?
сделает еще кое-что интересное, с объектом класса имеющего виртуальные методы, а что именно и к каким последствиям это приведет - повод для размышления и соотв. более глубокого понимания того что вы пишите.
{
public:
int var1=0;
float var2=0;
char var3=0;
};
Вот и возник вопрос, как автоматизировать, чтоб не ручками каждую переменную обнулять?
Вариант 2 - самый правильный.
Вариант 3 - неверный и потенциально опасный.
{
public:
int var1=0;
float var2=0;
char var3=0;
};
А тем плохо, что в С++ такого не может быть.
Кстати, что ты подразумевал под "при создании" ?
Разберись с терминологией. То, что ты приводишь, это не "создание", а "определение" класса (как я уже сказал, невалидное).
Вариант 2:
{
public:
int var1;
float var2;
char var3;
test(): var1(0), var2(0), var3(0){}
~test(){}
};
А если параметров 100?
Если тебе в башку пришла здравая идея создать класс с сотней элементов, то и перечисляй их все 100 в конструкторе.
гробится член var типа string.
Кстати std::string сам "обнулится", своим конструктором, его инициировать пустой строкой не нужно.
Примерно так. Лень - двигатель прогресса. :)
И к тому же вопрос относится к сопровождению кода. Допустим в конструкторе по умолчанию класс всегда инициализирует себя нулевыми значениями. Было бы удобно, если бы в класс можно было бы добавлять новые члены, каждый раз не переписывая конструктор, а пользоваться для этого неким автоматизированным способом.
К тому же не обязательно чтобы класс имел все эти 100 переменных в публичном доступе... Они вполне могут быть использованы для внутренних целей.
Это к вопросу о практичности..
Если у нас массив из 1000 элементов, не будем же мы писать код вручную инициализируя каждый элемент.
Для этого применяется или цикл с перебором или функция операции с памятью.
Но поскольку в классе переменные имеют разное имя, то их перебор в цикле не представляется мне возможным.
Остается вопрос - как автоматизировать обнуление.
Было бы удобно, если бы в класс можно было бы добавлять новые члены, каждый раз не переписывая конструктор, а пользоваться для этого неким автоматизированным способом.
Самый простой способ дописывать инициализацию в конструктор.
Ты добавляешь члены пачками по 100 штук? :)
Если у тебя класс таких размеров и разрастается с такой скоростью, то проблему надо решать не "автоматизированием обнуления", а переработкой архитектуры, т.к. не должно/не может такого быть в нормальной программе.
К тому же не обязательно чтобы класс имел все эти 100 переменных в публичном доступе... Они вполне могут быть использованы для внутренних целей.
А какая разница в данном случае public или private?
Если у нас массив из 1000 элементов, не будем же мы писать код вручную инициализируя каждый элемент.
Для этого применяется или цикл с перебором или функция операции с памятью.
Ну на то он и массив.
Но поскольку в классе переменные имеют разное имя, то их перебор в цикле не представляется мне возможным.
Есть вариант, но он будет значительно сложнее, чем просто самому указать в конструкторе.
Остается вопрос - как автоматизировать обнуление.
НИКАК!
Игра не стоит свеч. Ты высасываешь проблему из пальца.
Используй членами класса типы, инициализирующие себя сами.
Следущая задача видимо будет: как не определять конструктор/деструктор у класса или т.п. :)
Плз., а можно с этого места чуть-чуть поподробней? [COLOR="Gray"]Исключительно в целях удовлетворения моего похотливого любопытства.....[/COLOR]
{
public:
TAny() : var() {};
T var;
inline operator T&() { return var; };
// еще TAny(const T&) как минимум, лень все описывать - смысл ясен
};
class CSome
{
public:
CSome() {};
private:
TAny<int> i; // вместо int i
};
И так для всех полей. Будет вызван конструктор по умолчанию.
Несколько вариантов:
1. Создать свой предкомпилятор, как это сделано в Qt.
2. Использовать членами класса типы, инициализирующие себя сами. Примерно так, как это показал _const_.
3. При добавлении поля, автоматически добавлять его в некоторый список. Список скорее всего будет compiled-time. Далее в конструкторе просто "пробегаться" по списку и инициализировать поля. В кавычках, т.к. на самом деле пробежка будет не настоящая (не цикл), т.к. список будет не run-time. Выглядеть определение таких полей будет так:
public:
SomeClass() { // конструктор класса
AUTO_INIT(); // инициализация полей пробежкой по списку
}
// определение полей класса с автоматическим добавлением в список
AUTO_INIT_BEGIN
AUTO_INIT_FIELD(int, i)
AUTO_INIT_FIELD(char, j)
AUTO_INIT_END
};
Как реализовать такую штуку средствами С++ предлагаю подумать самим.
Хорошая разминка для мозгов. На самом деле, все элементарно.
Обращаю внимание, что под списком я понимаю не std::list или иные run-time списки, это некий мета-список.
public:
SomeClass() { // конструктор класса
AUTO_INIT(); // инициализация полей пробежкой по списку
}
// определение полей класса с автоматическим добавлением в список
AUTO_INIT_BEGIN
AUTO_INIT_FIELD(int, i)
AUTO_INIT_FIELD(char, j)
AUTO_INIT_END
};
Как реализовать такую штуку средствами С++ предлагаю подумать самим.
Хорошая разминка для мозгов. На самом деле, все элементарно.
Ну что, кто-нибудь предложит вариант реализации? :)
Если это реально, то интересно будет посмотреть :)
Т.е.
int i;
char j;
Иначе, задачу можно было бы решить использованием шаблонной обертки...
Т.е.
int i;
char j;
Да, но этот "явный вид" реализован внутри макросов, т.е. вот это полноценный законченный пример определения класса со всеми его (двумя: i, j) полями:
public:
SomeClass() { // конструктор класса
AUTO_INIT(); // инициализация полей пробежкой по списку
}
// определение полей класса с автоматическим добавлением в список
AUTO_INIT_BEGIN
AUTO_INIT_FIELD(int, i)
AUTO_INIT_FIELD(char, j)
AUTO_INIT_END
};
Иначе, задачу можно было бы решить использованием шаблонной обертки...
Да, это был бы вариант 2:
2. Использовать членами класса типы, инициализирующие себя сами. Примерно так, как это показал _const_.
Дополительных затрат ресурсов и засорения имен также не должно быть? (например, для определение вспомогательных объектов).
Если бы в С++ можно было делать вложенное макроопределение, то список можно было бы организовать с помощью него. Что-то вроде:
Но только цепочку надо строить до конструктора.
Дополительных затрат ресурсов и засорения имен также не должно быть? (например, для определение вспомогательных объектов).
Для начала давайте хоть как-нибудь... :)
Главное, чтоб пока все было скрыто от пользователя за макросами.
P.S. но в принципе есть способ без затрат ресурсов.
Я отталкивался от того, что определения полей макросом AUTO_INIT_FIELD отличаются только типом и именем переменной. А для создания цепочки необходимо "зацепиться" за информацию в этом макросе. А также, AUTO_INIT должен выполнять заранее определенные действия, не зависящие от включенных полей. Их этих соображений решил, что цепочку можно сделать вложенными вызовами методов, отличающихся друг от друга именем, а имя генерировать по информации макроса так, чтобы каждое в отдельности было уникальным (например init_i()). Начинается цепочка с заранее известной функции, скажем void init_begin(). В ней начинается вызов цепочки. Следовательно, в ее тело первый макрос AUTO_INIT_FIELD вписывает вызов своего уникального метода, определяет переменную и начинает описание этого уникального метода, завершается описание либо следующим вызовом AUTO_INIT_FIELD, который также добавляет вызов своего уникального метода, либо макросом AUTO_INIT_END, который просто закрывает метод.
#define AUTO_INIT_BEGIN void init_begin() {
#define AUTO_INIT_FIELD(TYPE, VAR)\
init_##VAR();\
}\
TYPE VAR;\
void init_##VAR()\
{\
VAR = TYPE();
#define AUTO_INIT_END }
class SomeClass {
public:
SomeClass() { // конструктор класса
AUTO_INIT(); // инициализация полей пробежкой по списку
}
// определение полей класса с автоматическим добавлением в список
AUTO_INIT_BEGIN
AUTO_INIT_FIELD(int, i)
AUTO_INIT_FIELD(char, j)
AUTO_INIT_END
};
все функции нормальный компилятор сделает подставляемыми и накладных расходов на вызовы не должно возникнуть.
#define AUTO_INIT_BEGIN void auto_init() {
#define AUTO_INIT_FIELD_VAL(type, name, val) auto_init_##name(); } type name; void auto_init_##name() { name = val;
#define AUTO_INIT_FIELD(type, name) AUTO_INIT_FIELD_VAL(type, name, type())
#define AUTO_INIT_END }