Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Инициализация статических компонентов класса


Автор: SABROG 19.8.2007, 19:08
Читаю в книге пример инициализации статического компонента класса

Код

class list
{
public:
static list *begin;
};
...
list *list::begin = NULL;


И вот часть где идет присваивание NULL я никак не могу догнать. Я бы понял такую запись:

Код

list::begin = NULL;


Хоть и не знаю на сколько она правильная. Можете мне разъяснить где тут что к чему относится ?

Автор: DjoNIK 19.8.2007, 19:18
Первый вариант правильный, и второй тоже (может быть в каком-нибудь другом языке, но не в C++)
Всё очень просто:
Код

list *

говоришь компилятору о том, что инициализируемая переменная будет указателем на list;
Код

list::

говоришь о том, что эта переменная будет членом класса list;
Код

begin = NULL;

собственно задаешь значение переменной.

PS: Что за книгу читаешь?  smile 

Автор: SABROG 19.8.2007, 19:36
Книжка "Язык С++ В.В. Подбельский 4 издание 1999г.".

Без явного указания типа компилятор ругается:

Код

src\main.cpp:9: error: expected constructor, destructor, or type conversion before '=' token


Это фича такая чтоль или gcc тупит и не может определить тип без явного указания ?

Автор: DjoNIK 19.8.2007, 19:53
Цитата

Если я правильно понимаю, то часть "list *" - явное указание типа члена класса

Ты правильно понял!
Цитата

несмотря на то, что компилятор и так уже знает какого типа этот член

Действительно, компилятор знает, что у его переменной-члена тип "list *". НО(!) откуда ты думаешь, что компилятор узнает какой именно член класса ты инициализируешь? А ведь для этого  и нужно указать тип(переменная-член)/тип возвращаемого значения(функция-член класса), название класса, поставить двоеточие, указать имя переменной-члена/функции-члена класса и задать значение если это переменная.

Автор: zkv 19.8.2007, 20:07
Цитата(DjoNIK @  19.8.2007,  19:53 Найти цитируемый пост)
компилятор узнает какой именно член класса ты инициализируешь?

а что есть возможность с кем-то перепутать? 
Только типом возвращаемого значения сигнатуры не имеют права отличаться.

Автор: DjoNIK 19.8.2007, 20:08
Судя по твоим вопросам, ты не совсем правильно понимаешьсути происходящего. Я сам только начинаю путешествие в этот волшебный мир программирования smile, но смело могу посоветовать перечитать эту книгу более внимательно! Либо как вариант взять более свежее издание, например на Ozon-е сей час лежит пятое(!). И уж как самый крайний вариант прочитать другую книгу, я бы посоветовал Р. Лафоре "Объектно-ориентированное программирование в C++" (сам её дочитываю). Благо в нете полно электронных версий - качай, распечатывай да читай. Но это всё IMHO и  smile 

Автор: SABROG 19.8.2007, 20:11
Ну как же. Если компилятор не позволяет иметь в одном контексте переменную и функцию с одним и тем же именем, а значит по имени четко можно определить переменная это или метод.

Вот что будет если завести метод и переменную с одним именем:

Код

include/TForm.h:113: error: declaration of `void TForm::a()'
include/TForm.h:88: error: conflicts with previous declaration `int TForm::a'
src\main.cpp:9: error: `int TForm::a' is not a static member of `class TForm'

Автор: DjoNIK 19.8.2007, 20:17
Цитата(zkv @ 19.8.2007,  20:07)
а что есть возможность с кем-то перепутать?

Вообще-то да, в данном случае не с кем. Но если учесть, что человек будет иметь дело с функциями(о чем ты и говоришь), то здесь тип нужно указать. Но ведь и по семантике одновременного объявления и определения в C++ положено так:
Код

ТИП ИДЕНТИФИКАТОР_ПЕРЕМЕННОЙ = ЗНАЧЕНИЕ;

при условии, что ИДЕНТИФИКАТОР_ПЕРЕМЕННОЙ для членов класса содержит и идентификатор самого класса с разрешением зоны видимости (::).
Или я не прав?

Автор: bsa 19.8.2007, 20:18
SABROG, привила есть правила. По правилам языка Си, если ты написал:
Код
extern int h;
, то потом ты где-то проекте должен написать:
Код
int h;
Обрати внимание, что тип опять указывается, хоть нельзя 2 раза объявить переменную с одним именем.
Статическая переменная класса в C++ это тоже в некотором роде extern. Только оформлен он немного подругому.
А extern - это не объявление переменной. А декларация того, что она где-то определена. Определение это как раз и есть твой:
Код
list* list::begin = NULL;

Автор: SABROG 19.8.2007, 20:23
Ну если больше аргументов нет, то я могу сделать вывод, что это недоработка стандарта, т.к. реальной пользы от соблюдения этого правила нет, только путаница.

Автор: DjoNIK 19.8.2007, 20:30
Цитата

я могу сделать вывод, что это недоработка стандарта

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

Автор: SABROG 19.8.2007, 20:40
И все же, мне кажется, дело вовсе не в скорости, т.к. везде и повсеместно тип переменных опускается после первого определения. Или вот я не могу понять почему необходимо писать прототипы функций/определения, если их прототипы дублируются в реализации:

Код

class a
{
public: 
int print(char *);
};
int a::print(char *b)
{
...
return 0;
}


Т.е. информации достаточно, чтобы 1) соотнести метод к конкретному классу 2) выцепить типы параметров
Хотя может я и упускаю, какие-нибудь исключительные ситуации.

Автор: DjoNIK 19.8.2007, 21:12
Цитата

везде и повсеместно тип переменных опускается после первого определения

Кстати да! Но ты не забывай, что определение класса - не одно и то же, что определение объекта класса!!!
При определение класса ты как бы сообщаешь компилятору скелет будующих объектов, но ПАМЯТЬ при этом НЕ ВЫДЕЛЯЕТСЯ.
В противовес этому определение объекта выделяет память под уже известный каркас(наш класс). Но если ты определишь объект класса, но забудешь определить статический член класса, то этот член будет содержать случайное значение(в случае объявления локальным) или ноль(если объект глобален).

Автор: SABROG 19.8.2007, 21:18
Да, только на эту ситуацию наличие или отсутствие типа статического члена класса, за пределами этого класса никак не влияет. Прописав явную инициализацию объекта, еще и указав какому классу он принадлежит компилятор знает чего и сколько вешать в граммах, причем на скорость компиляции это влияет в любом случае, т.к. происходит сравнение типов, я уже попробовал вместо int напсать unsigned int и получил ошибочку, т.е. компилятор в любом случае проверяет типы.

Автор: DjoNIK 19.8.2007, 21:34
Цитата

компилятор в любом случае проверяет типы

Само собой, но чем более формализована семантика языка, тем лучше для скорости компиляции(кажеться, что я повторяюсь smile ).
Одно дело получить
Код

ТИП ИДЕНТИФИКАТОР_ПЕРЕМЕННОЙ = ЗНАЧЕНИЕ;

и совсем другое
Код

ИДЕНТИФИКАТОР_ПЕРЕМЕННОЙ = ЗНАЧЕНИЕ;


Ну если ты не согласен, то можно аргументировать и отослать в комитет стандартизации до 2009 года  smile .
А вообще-то мы давно отошли от темы топика Господа!

Автор: SABROG 19.8.2007, 22:38
Если я все правильно понял, то причины этого следующие. При определении статического члена необходимо указать его местоположение, если хотите, в памяти, ну или соотнести его принадлежность к определенному блоку/контексту, точно также как это делается при объявлении любой новой переменной, объект на то и статический, что подразумевает то, что место в памяти под него не может быть выделено динамически через оператор new или как статический временный объект внутри любого блока. Таким образом, если объект не может быть создан динамически внутри блока/функции, то он должен быть внешним, вынос местоположения объекта из блока наружу делает объект еще и глобальным. При желании, его можно еще и проинициализировать, но как я понял, делать это совсем не обязательно:

Код

int TForm::a;
    int main(int argc, char *argv[])
    {
    TForm::a = 1;
    }


А вот что бывает, если не указать местоположение статического объекта:

Код

./tmp\main.o(.text+0x144): In function `Z5qMainiPPc':
main.cpp:12: undefined reference to `TForm::a'

Автор: DjoNIK 20.8.2007, 08:41
Код

int TForm::a;
    int main(int argc, char *argv[])
    {
    TForm::a = 1;
    }

Если ты таким образом инициализируешь статическую переменную a, то в строке 1 она станет равна 0(!), т.к. определена в глобальном пространстве имен. А если ты предпологаешь, что начальное значение должно быть 1, то первая строка должна быть
Код

int TForm::a = 1;

Иначе, по закону Мерфи, кто-нибудь(а может быть и ты сам)  создав экземпляр класса забудет проинициализировать TForm::a еденицей, и будет его использовать с неправильным начальным значением, что может привести к неожиданным результатам.

Автор: bsa 20.8.2007, 10:10
Цитата(DjoNIK @ 20.8.2007,  08:41)
Код

int TForm::a;
    int main(int argc, char *argv[])
    {
    TForm::a = 1;
    }

Если ты таким образом инициализируешь статическую переменную a, то в строке 1 она станет равна 0(!), т.к. определена в глобальном пространстве имен. А если ты предпологаешь, что начальное значение должно быть 1, то первая строка должна быть
Код

int TForm::a = 1;

Иначе, по закону Мерфи, кто-нибудь(а может быть и ты сам)  создав экземпляр класса забудет проинициализировать TForm::a еденицей, и будет его использовать с неправильным начальным значением, что может привести к неожиданным результатам.

Позволю себе не согласиться с первым утверждением. Значение a на первой строке совсем не обязательно будет равно 0.

Автор: SABROG 20.8.2007, 10:20
Так и есть. Я хочу лишь показать, что эта конструкция раскладывается на две составляющие - определение и инициализацию, чтобы подтвердить свою догадку о необходимости прописывать тип переменной повторно.

Автор: bsa 20.8.2007, 10:42
Цитата(SABROG @ 20.8.2007,  10:20)
Так и есть. Я хочу лишь показать, что эта конструкция раскладывается на две составляющие - определение и инициализацию, чтобы подтвердить свою догадку о необходимости прописывать тип переменной повторно.

Вобщем да. Определение это list* list::begin; А инициализация - list::begin = NULL;

Автор: DjoNIK 20.8.2007, 13:29
Цитата

Позволю себе не согласиться с первым утверждением. Значение a на первой строке совсем не обязательно будет равно 0.

Можно конечно и не согласиться, но я отвечал так, как мне было изложенно в теории.
После твоих слов усомнился в своих убеждениях и проверил в MS VC++(2005).
Я всё-таки прав. Переменные определённые, но не проинициализированные в глобальном пространстве имен получают значение по дефолту ноль(ну или NULL для указателей).
Переменные же определённые, но не проинициализированные в локальном пространстве имен получают случайное значение.

Автор: bsa 20.8.2007, 14:11
Цитата(DjoNIK @ 20.8.2007,  13:29)
Я всё-таки прав. Переменные определённые, но не проинициализированные в глобальном пространстве имен получают значение по дефолту ноль(ну или NULL для указателей).
Переменные же определённые, но не проинициализированные в локальном пространстве имен получают случайное значение.

Это не есть доказательство. Приведи цитату из стандарта.

Автор: Fazil6 20.8.2007, 14:19
Цитата(bsa @  20.8.2007,  14:11 Найти цитируемый пост)
Это не есть доказательство. Приведи цитату из стандарта.


Страуструп. Язык программирования С++. 4.9.5.
Цитата

...
Если инициализатор не задан, то глобальным объектам ($ 4.9.4), объектам из пространства имен ($8.2) и локальным статическим объектам ($ 7.1.2, $10.2.4) (все они называются статическими объектами)  присваивается нулевое значение соответствующего типа...


Добавлено через 1 минуту и 45 секунд
автоматические и динамические объекты не инициализируются поумолчанию

Автор: bsa 20.8.2007, 15:51
спасибо. так уже лучше.   smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)