Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Наиболее элегантное обнуление членов класса при инициализации

590
01 января 2008 года
Gigahard
223 / / 03.04.2006
Часто возникают ситуации, когда при создании класса с пустым конструктором (без параметров) требуется установить все переменные класса в ноль.
Что нам позволяет синтаксис C++:

Вариант 1:
Код:
class test
{
    public:
    int var1;
    float var2;
    char var3;

    test()
    {
         var1=0;
         var2=0;
         var3=0;
     }
     ~test(){}
};


Вариант 2:
 
Код:
class test
{
    public:
    int var1;
    float var2;
    char var3;

    test(): var1(0), var2(0), var3(0){}
     ~test(){}
};


Вариант 3:
 
Код:
class test
{
    public:
    int var1;
    float var2;
    char var3;

    test(){ memset(this, NULL, sizeof(*this); }
     ~test(){}
};



В примерах приведено только по три члена класса, что в данном случае позволяет легко проинициализировать каждый из них вручную. А если параметров 100? Казалось бы тут может выручить третий вариант, который берет и забивает всю выделенную под класс область памяти нулями. Но третий вариант не прокатывает, если члены класса представляют собой другие классы в особенности испльзующие указатели.
К примеру, в данном случае:

 
Код:
class test
{
    public:
    string var;

    test(){ memset(this, NULL, sizeof(*this); }
     ~test(){}
};


гробится член var типа string.

Как выход, можно создавать такие члены класса динамически, после обнуления, но это опять возврат к первому варианту.

Вот и возник вопрос, как автоматизировать, чтоб не ручками каждую переменную обнулять?
260
01 января 2008 года
Ramon
1.1K / / 16.08.2003
конструкция
 
Код:
memset(this, NULL, sizeof(*this);


сделает еще кое-что интересное, с объектом класса имеющего виртуальные методы, а что именно и к каким последствиям это приведет - повод для размышления и соотв. более глубокого понимания того что вы пишите.
360
01 января 2008 года
P*t*
474 / / 15.02.2007
А чем плохо при создании обнулять?
 
Код:
class test
{
    public:
    int var1=0;
    float var2=0;
    char var3=0;

};
3
01 января 2008 года
Green
4.8K / / 20.01.2000
Цитата: Gigahard

Вот и возник вопрос, как автоматизировать, чтоб не ручками каждую переменную обнулять?


Вариант 2 - самый правильный.
Вариант 3 - неверный и потенциально опасный.

Цитата: P*t*
А чем плохо при создании обнулять?
 
Код:
class test
{
    public:
    int var1=0;
    float var2=0;
    char var3=0;

};


А тем плохо, что в С++ такого не может быть.

Кстати, что ты подразумевал под "при создании" ?
Разберись с терминологией. То, что ты приводишь, это не "создание", а "определение" класса (как я уже сказал, невалидное).

398
02 января 2008 года
Alexandoros
630 / / 21.10.2005
Цитата: Gigahard


Вариант 2:
 
Код:
class test
{
    public:
    int var1;
    float var2;
    char var3;

    test(): var1(0), var2(0), var3(0){}
     ~test(){}
};


А если параметров 100?



Если тебе в башку пришла здравая идея создать класс с сотней элементов, то и перечисляй их все 100 в конструкторе.


Цитата: Gigahard

гробится член var типа string.



Кстати std::string сам "обнулится", своим конструктором, его инициировать пустой строкой не нужно.

22K
07 января 2008 года
deninok
16 / / 01.03.2007
Ну, в принципе можно представить себе класс, содержащий 100 членов-данных (гипотетически их ведь может быть хоть 1000), хотя, по моему глубокому убеждению, подобный класс вряд ли можно назвать рациональным (в контексте ООП). Посему вопрос автора темы можно рассматривать скорее как чисто теоретический, нежели практический.
590
09 января 2008 года
Gigahard
223 / / 03.04.2006
Цитата: deninok
Посему вопрос автора темы можно рассматривать скорее как чисто теоретический, нежели практический.



Примерно так. Лень - двигатель прогресса. :)
И к тому же вопрос относится к сопровождению кода. Допустим в конструкторе по умолчанию класс всегда инициализирует себя нулевыми значениями. Было бы удобно, если бы в класс можно было бы добавлять новые члены, каждый раз не переписывая конструктор, а пользоваться для этого неким автоматизированным способом.
К тому же не обязательно чтобы класс имел все эти 100 переменных в публичном доступе... Они вполне могут быть использованы для внутренних целей.
Это к вопросу о практичности..

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

Но поскольку в классе переменные имеют разное имя, то их перебор в цикле не представляется мне возможным.

Остается вопрос - как автоматизировать обнуление.

3
09 января 2008 года
Green
4.8K / / 20.01.2000
Цитата: Gigahard

Было бы удобно, если бы в класс можно было бы добавлять новые члены, каждый раз не переписывая конструктор, а пользоваться для этого неким автоматизированным способом.


Самый простой способ дописывать инициализацию в конструктор.
Ты добавляешь члены пачками по 100 штук? :)
Если у тебя класс таких размеров и разрастается с такой скоростью, то проблему надо решать не "автоматизированием обнуления", а переработкой архитектуры, т.к. не должно/не может такого быть в нормальной программе.

Цитата: Gigahard

К тому же не обязательно чтобы класс имел все эти 100 переменных в публичном доступе... Они вполне могут быть использованы для внутренних целей.


А какая разница в данном случае public или private?

Цитата: Gigahard

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


Ну на то он и массив.

Цитата: Gigahard

Но поскольку в классе переменные имеют разное имя, то их перебор в цикле не представляется мне возможным.


Есть вариант, но он будет значительно сложнее, чем просто самому указать в конструкторе.

Цитата: Gigahard

Остается вопрос - как автоматизировать обнуление.


НИКАК!
Игра не стоит свеч. Ты высасываешь проблему из пальца.

Используй членами класса типы, инициализирующие себя сами.

Следущая задача видимо будет: как не определять конструктор/деструктор у класса или т.п. :)

1.9K
10 января 2008 года
InterWen
331 / / 16.09.2006
Цитата:
Есть вариант, но он будет значительно сложнее, чем просто самому указать в конструкторе.



Плз., а можно с этого места чуть-чуть поподробней? [COLOR="Gray"]Исключительно в целях удовлетворения моего похотливого любопытства.....[/COLOR]

1.8K
10 января 2008 года
_const_
229 / / 26.11.2003
Ну если очень надо, то можно так:
Код:
template<class T> class TAny
{
public:
    TAny() : var() {};
    T var;
    inline operator T&() { return var; };
// еще TAny(const T&) как минимум, лень все описывать - смысл ясен
};

class CSome
{
public:
    CSome() {};
private:
    TAny<int> i;    // вместо int i
};

И так для всех полей. Будет вызван конструктор по умолчанию.
3
10 января 2008 года
Green
4.8K / / 20.01.2000
Цитата: InterWen
Плз., а можно с этого места чуть-чуть поподробней? [COLOR="Gray"]Исключительно в целях удовлетворения моего похотливого любопытства.....[/COLOR]


Несколько вариантов:
1. Создать свой предкомпилятор, как это сделано в Qt.
2. Использовать членами класса типы, инициализирующие себя сами. Примерно так, как это показал _const_.
3. При добавлении поля, автоматически добавлять его в некоторый список. Список скорее всего будет compiled-time. Далее в конструкторе просто "пробегаться" по списку и инициализировать поля. В кавычках, т.к. на самом деле пробежка будет не настоящая (не цикл), т.к. список будет не run-time. Выглядеть определение таких полей будет так:

Код:
class SomeClass {
public:
    SomeClass() {          // конструктор класса
        AUTO_INIT();       // инициализация полей пробежкой по списку
    }

    // определение полей класса с автоматическим добавлением в список
    AUTO_INIT_BEGIN    
        AUTO_INIT_FIELD(int, i)
        AUTO_INIT_FIELD(char, j)
    AUTO_INIT_END
};

Как реализовать такую штуку средствами С++ предлагаю подумать самим.
Хорошая разминка для мозгов. На самом деле, все элементарно.
Обращаю внимание, что под списком я понимаю не std::list или иные run-time списки, это некий мета-список.
3
14 января 2008 года
Green
4.8K / / 20.01.2000
Цитата: Green

Код:
class SomeClass {
public:
    SomeClass() {          // конструктор класса
        AUTO_INIT();       // инициализация полей пробежкой по списку
    }

    // определение полей класса с автоматическим добавлением в список
    AUTO_INIT_BEGIN    
        AUTO_INIT_FIELD(int, i)
        AUTO_INIT_FIELD(char, j)
    AUTO_INIT_END
};

Как реализовать такую штуку средствами С++ предлагаю подумать самим.
Хорошая разминка для мозгов. На самом деле, все элементарно.


Ну что, кто-нибудь предложит вариант реализации? :)

255
14 января 2008 года
Dart Bobr
1.4K / / 09.04.2004
м-м-м.. мне кажется это делается при помощи паттерна фабрики обьектов.. хотя, интересно посмотреть на то, кто что предложит..
3
14 января 2008 года
Green
4.8K / / 20.01.2000
да нет, уже никаких паттернов, переходим к реализации конкретных макросов: AUTO_INIT_BEGIN, AUTO_INIT_FIELD и т.д.
255
14 января 2008 года
Dart Bobr
1.4K / / 09.04.2004
Хмм.. Ну, учитывая что поля базового класса могут также быть классами(и так далее), которые так же само необходимо инициализировать, я сложно себе это представляю без паттерна, да и вообще в compile-time..
Если это реально, то интересно будет посмотреть :)
505
14 января 2008 года
vAC
343 / / 28.02.2006
Насколько я понимаю, при этом стоит условие, что объявление полей должно быть в явном виде?
Т.е.
int i;
char j;

Иначе, задачу можно было бы решить использованием шаблонной обертки...
3
14 января 2008 года
Green
4.8K / / 20.01.2000
Цитата: vAC
Насколько я понимаю, при этом стоит условие, что объявление полей должно быть в явном виде?
Т.е.
int i;
char j;


Да, но этот "явный вид" реализован внутри макросов, т.е. вот это полноценный законченный пример определения класса со всеми его (двумя: i, j) полями:

Код:
class SomeClass {
public:
    SomeClass() {          // конструктор класса
        AUTO_INIT();       // инициализация полей пробежкой по списку
    }

    // определение полей класса с автоматическим добавлением в список
    AUTO_INIT_BEGIN    
        AUTO_INIT_FIELD(int, i)
        AUTO_INIT_FIELD(char, j)
    AUTO_INIT_END
};


Цитата: vAC

Иначе, задачу можно было бы решить использованием шаблонной обертки...


Да, это был бы вариант 2:

Цитата: Green

2. Использовать членами класса типы, инициализирующие себя сами. Примерно так, как это показал _const_.

505
14 января 2008 года
vAC
343 / / 28.02.2006
И еще, забыл совсем...
Дополительных затрат ресурсов и засорения имен также не должно быть? (например, для определение вспомогательных объектов).

Если бы в С++ можно было делать вложенное макроопределение, то список можно было бы организовать с помощью него. Что-то вроде:
 
Код:
#define AUTO_INIT_FIELD(TYPE, NAME) TYPE NAME; #define AUTO_INIT AUTO_INIT NAME = ...;

Но только цепочку надо строить до конструктора.
3
14 января 2008 года
Green
4.8K / / 20.01.2000
Цитата: vAC
И еще, забыл совсем...
Дополительных затрат ресурсов и засорения имен также не должно быть? (например, для определение вспомогательных объектов).


Для начала давайте хоть как-нибудь... :)
Главное, чтоб пока все было скрыто от пользователя за макросами.

P.S. но в принципе есть способ без затрат ресурсов.

505
15 января 2008 года
vAC
343 / / 28.02.2006
Без засорения пока не сообразил...
Я отталкивался от того, что определения полей макросом AUTO_INIT_FIELD отличаются только типом и именем переменной. А для создания цепочки необходимо "зацепиться" за информацию в этом макросе. А также, AUTO_INIT должен выполнять заранее определенные действия, не зависящие от включенных полей. Их этих соображений решил, что цепочку можно сделать вложенными вызовами методов, отличающихся друг от друга именем, а имя генерировать по информации макроса так, чтобы каждое в отдельности было уникальным (например init_i()). Начинается цепочка с заранее известной функции, скажем void init_begin(). В ней начинается вызов цепочки. Следовательно, в ее тело первый макрос AUTO_INIT_FIELD вписывает вызов своего уникального метода, определяет переменную и начинает описание этого уникального метода, завершается описание либо следующим вызовом AUTO_INIT_FIELD, который также добавляет вызов своего уникального метода, либо макросом AUTO_INIT_END, который просто закрывает метод.

Код:
#define AUTO_INIT init_begin
#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
};

все функции нормальный компилятор сделает подставляемыми и накладных расходов на вызовы не должно возникнуть.
3
15 января 2008 года
Green
4.8K / / 20.01.2000
Да, у меня такой же набор макросов. Я только ещё ввел макрос AUTO_INIT_FIELD_VAL, чтоб можно было инициализировать любым значением:
 
Код:
#define AUTO_INIT()                             auto_init()
#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                           }
505
15 января 2008 года
vAC
343 / / 28.02.2006
Green, как вы считаете, можно еще что-то придумать?
3
15 января 2008 года
Green
4.8K / / 20.01.2000
Думаю, при таком постороении ничего лучше уже не придумать.
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог