Здравствуйте, WirBi, Вы писали:
WB>Так вот вопрос, где именно должны быть определены функции и данные? Как это правильно использовать? И где об этом подробно почитать?
Подробно — здесь
Ссылки [1-...] это стандарт С++
Глобальные (и статические, как локальные, так и нет) переменные компилятор размещает в статической памяти, и время жизни таких переменных совпадает с временем выполнении программы. В стандарте [1-3.6.2] указано, что такие переменные, в отличие от локальных (не статических) и динамических, неявно инициализируются по умолчанию, причем до начала выполнения функции main. Такая инициализация называется статической, в отличие от динамической, задаваемой программистом явно. Встроенные типы инициализируются нулями. Для объектов реализованных классов инициализация нулями обычно невозможна, поэтому для глобальных объектов не встроенных типов вызывается конструктор по умолчанию (без аргументов). Если в классе его нет, то возникает ошибка трансляции. Обратите внимание — вызывается именно конструктор без аргументов, а не конструктор инициализации. Последний применяется для явной инициализации.
В рамках одного модуля порядок инициализации переменных встроенных типов определяется порядком объявления. Конструкторы для создания и инициализации глобального объекта тоже вызываются в порядке объявлений объектов. Деструкторы вызываются перед завершением программы в обратном порядке.
Продолжаем.
Самое время вспомнить о статических элементах класса [1-9.4]. Вообще-то статическим элементом класса может быть как поле, так и метод. Естественно, статические методы предназначены, в первую очередь, для манипуляции статическими полями.
К статическим методам мы еще вернемся позднее, а сейчас займемся статическими полями и их инициализацией. Б. Страуструп [2, c.274] определяет статическое поле как поле, которое "является частью класса, но не является частью объекта этого класса". Это означает, что статические поля класса, как и все переменные, размещаемые в статической памяти, создаются в единственном экземпляре, независимо от количества определяемых в программе объектов. Все объекты (даже созданные динамически) разделяют единственную копию статических полей [1 9.4.2]. Более того, все наследники тоже разделяют эту единственную копию. При этом в класс включается только объявление, а определение статического поля выполняется отдельно, за пределами класса. Обычно определение включается в модуль с реализацией класса. Единственным исключением из этого правила являются целочисленные статические константы, которые разрешено определять непосредственно в классе.
Естественно, определение должно быть единственным в программе, иначе компоновщик (не компилятор) сообщит о повторном определении. Если определение отсутствует, то тоже получим ошибку компоновки.
При определении поля выполняется и его инициализация. Если статическое поле является константным, то инициализация обязательна. Инициализация статических полей выполняется точно так же, как и инициализация глобальных и статических переменных [1-9.4.2/7]. Для не константных полей элементарных типов при отсутствии явной инициализации выполняется обнуление. Для статических полей не элементарных типов, естественно, вызываются конструкторы: либо явным образом конструктор инициализации, либо, при отсутствии явной инициализации, конструктор по умолчанию.
Продемонстрируем все это на элементарном примере (листинг 6.13).
//Листинг 6.13. Статические поля
//--------------------------файл-Person.h#ifndef PERSON // страж#define PERSON
#include <string>
class Person // демонстрационный класс
{ std::string Fio; // неэлементарный типint year;
public:
Person() // конструктор по умолчанию
: Fio("Lippman"), year(1953) {}
Person(std::string fio, int y) // конструктор инициализации
{ Fio = fio; year = y; }
void print() const; // вывод полей на экран
};
#endif//--------------------------файл-StaticField.h#include"Person.h"class StaticField
{ static const int m01 = 1; // инициализированная константаstatic const int m02; // неинициализированное константное полеstatic const double m03; // константное поле не целого типаstatic int m04; // неконстантное поле целого типаstatic double m05; // неконстантное поле не целого типаstatic const Person p; // константное поле неэлементарного типаstatic Person t; // неконстантное поле неэлементарного типаpublic:
static void print(); // статический метод
};
//--------------------------файл-StaticField.cpp#include <iostream>
using std::cout;
using std::endl;
#include"StaticField.h";
// определение статических полейconst int StaticField::m02 = 2; // обязательная инициализация const double StaticField::m03 = 3.1; // обязательная инициализацияint StaticField::m04; // инициализация по умолчанию (ноль)double StaticField::m05; // инициализация по умолчанию (ноль)const Person StaticField::p("Kupaev", 1999); // конструктор инициализации
Person StaticField::t; // конструктор по умолчанию
// определение статического методаvoid StaticField::print()
{ cout << m01 << endl;
cout << m02 << endl;
cout << m03 << endl;
cout << m04 << endl;
cout << m05 << endl;
p.print();
t.print();
}
void Person::print() const// константный нестатический метод
{ cout << Fio <<',' << year << endl; }
//--------------------------файл-main.cpp#include <iostream>
using namespace std;
#include"StaticField.h"int main()
{ StaticField::print(); // вывод значений статических полейreturn 0;
}
В этой программе класс Person написан только для демонстрации статических полей не элементарного типа. В нем определено два конструктора: по умолчанию и инициализации, — чтобы продемонстрировать вызовы при инициализации статических полей типа Person.
В файле StaticField.h прописано определение класса StaticField, где объявляется ряд статических полей, константных и не константных. Для вывода значений на экран объявляется статический метод print(). Определение статических полей выполняется в файле реализации StaticField.cpp. Для константных статических полей (m02 и m03) инициализацию писать обязательно. Не константные статические поля элементарных типов (m04 и m05) инициализируются по умолчанию — выполняется обнуление, как для глобальных или статических переменных.
И наконец, определение статических полей типа Person демонстрирует вызов конструкторов. Поле p должно быть обязательно проинициализировано, так как является константным. Именно для его инициализации в классе Person реализован конструктор инициализации. Поле t не является константным, поэтому определяется без явной инициализации. Однако для него вызывается конструктор по умолчанию, что можно наблюдать при вызове статического метода print() в главной программе. Программа выведет на экран
1 // m01 – константа, инициализируется в классе
2 // m02 – константа, инициализируется явно
3.1 // m03 – константа, инициализируется явно
0 // m04 – не константа, инициализируется по умолчанию (0)
0 // m05 – не константа, инициализируется по умолчанию (0)
Kupaev,1999 // p — константа, инициализируется явно
Lippman,1953 // t — не константа, инициализируется неявно
Обратите внимание — статический метод работает, хотя не определено ни одного объекта типа StaticField. Именно поэтому вызов метода пишется с префиксом — именем класса
StaticField::print();
В отличие от этого метода, метод print() класса Person вызывается для конкретных объектов p и t.
Определения статических полей, на первый взгляд, противоречат всем принципам инкапсуляции: поля объявлены приватными, но значения им присваиваются "в открытую". Однако, во-первых, такая инициализация разрешена только в определении, которое обязан предоставить создатель класса, иначе программа не пройдет компоновку. Во-вторых, определение-то у нас единственное. Таким образом, механизм защиты данных работает по-прежнему — присвоить значение приватной статической переменной "в открытую" в другом месте не получится.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Libra:
Ошибки в терминах: > > Это значит что должен быть примерно вот такой код >
> //date_header.h file
> #ifndef _DATE_HEADER_
> #define _DATE_HEADER_
>
> //это ОБЪЯВЛЕНИЕ класса и его членов// нет, это ОПРЕДЕЛЕНИЕ класса, объявление вот: class Date;
> class Date {
> int d, m, y;
> static Date default_date;
> public:
> Date (int dd=0, int mm=0, int yy=0);
> // ...
> static void set_default (int, int, int);
> };
> #endif
> > >
> //date_implementtion.cpp file
>
> #include"date_header.h"
>
> //все что написано ниже это ОПРЕДЕЛЕНИЕ// ОПРЕДЕЛЕНИЯ ЧЛЕНОВ КЛАССА
>
> Date Date::default_date (16, 12, 1770);
>
> void Date::set_default (int d, int m, int y)
> {
> Date::default_date = Date (d, m, y);
> }
>
Я НЕ ВЕРЮ. Ты хочешь сказать, что старый компилятор не компилировал такое :
//date.hclass Date
{
public:
Date(int d); //скипаю месяц и годprivate:
int d_;
static Date defDate_;
static void SetDef(int d);
};
//test.cpp#include"date.h"
Date::Date(int d)
{
d_ = d ? d : defDate_.d_;
}
Date Date::defDate_(1);
void Date::SetDef(int d)
{
defDate_.d_ = d;
}
int main()
{
Date d(0);
return 0;
}
Что это за компилятор у тебя был тогда?
Of course, the code must be complete enough to compile and link.
//date_header.h file#ifndef _DATE_HEADER_
#define _DATE_HEADER_
//это ОБЪЯВЛЕНИЕ класса и его членовclass Date {
int d, m, y;
static Date default_date;
public:
Date (int dd=0, int mm=0, int yy=0);
// ...static void set_default (int, int, int);
};
#endif
//date_implementtion.cpp file#include"date_header.h"//все что написано ниже это ОПРЕДЕЛЕНИЕ
Date Date::default_date (16, 12, 1770);
void Date::set_default (int d, int m, int y)
{
Date::default_date = Date (d, m, y);
}
Species come and go, but the earth stands forever fast...
Здравствуйте, Libra, Вы писали:
L>Это значит что должен быть примерно вот такой код
//date_implementtion.cpp file#include"date_header.h"//все что написано ниже это ОПРЕДЕЛЕНИЕ
Date Date::default_date (16, 12, 1770);
void Date::set_default (int d, int m, int y)
{
Date::default_date = Date (d, m, y);
}
Но если я добавляю конструктор Date, как в книге, то:
//date_implementtion.cpp file#include"date_header.h"
Date::Date (int dd, int mm, int yy)
{
d = dd ? dd : default_date.d;
m = mm ? mm : default_date.m;
y = yy ? yy : default_date.y;
}
Date Date::default_date (16, 12, 1770);
void Date::set_default (int d, int m, int y)
{
Date::default_date = Date (d, m, y);
}
У меня при компиляции ошибка `dafault_date' undeclared (first use this function) в конструкторе Date.
Здравствуйте, WirBi, Вы писали:
WB>У меня при компиляции ошибка `dafault_date' undeclared (first use this function) в конструкторе Date.
Вот этот пример работает. Правда, я делал все в одном файле.
class Date {
int d, m, y;
static Date default_date;
public:
Date (int dd=0, int mm=0, int yy=0);
// ...static void set_default (int, int, int);
static void print()
{ std::cout << Date::default_date.y <<'.'
<< Date::default_date.m <<'.'
<< Date::default_date.d << std::endl;
}
};
Date Date::default_date = Date(16, 12, 1770);
Date::Date (int dd, int mm, int yy)
{
d = dd;
m = mm;
y = yy;
}
void Date::set_default (int dd, int mm, int yy)
{
Date::default_date.d = dd;
Date::default_date.m = mm;
Date::default_date.y = yy;
}
int main()
{ Date::print();
}
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Здравствуйте, folk, Вы писали:
F>Libra: F>Ошибки в терминах: >> >> Это значит что должен быть примерно вот такой код >>
>> //date_header.h file
>> #ifndef _DATE_HEADER_
>> #define _DATE_HEADER_
>>
>> //это ОБЪЯВЛЕНИЕ класса и его членов
F>// нет, это ОПРЕДЕЛЕНИЕ класса, объявление вот: class Date;
>> class Date {
>> int d, m, y;
>> static Date default_date;
>> public:
>> Date (int dd=0, int mm=0, int yy=0);
>> // ...
>> static void set_default (int, int, int);
>> };
>> #endif
>> >> >>
>> //date_implementtion.cpp file
>>
>> #include"date_header.h"
>>
>> //все что написано ниже это ОПРЕДЕЛЕНИЕ
F>// ОПРЕДЕЛЕНИЯ ЧЛЕНОВ КЛАССА
>>
>> Date Date::default_date (16, 12, 1770);
>>
>> void Date::set_default (int d, int m, int y)
>> {
>> Date::default_date = Date (d, m, y);
>> }
>>
согласен...
прошу прощения за то что пытался ввести в заблуждение
Species come and go, but the earth stands forever fast...
Здравствуйте, Lorenzo_LAMAS, Вы писали:
L_L>Я НЕ ВЕРЮ. Ты хочешь сказать, что старый компилятор не компилировал такое :
Код приведенный тобой компилируется. Проблемы возникли в другом месте, когда я добавил конструктор как в книге, посмотри, я приводил код.
L_L>Что это за компилятор у тебя был тогда?
Dev C++ 4.9.8.9 MinGW (GCC) 3.2 (mingw special 20020817-1)
WB>Код приведенный тобой компилируется. Проблемы возникли в другом месте, когда я добавил конструктор как в книге, посмотри, я приводил код.
Мой код практически идентичен твоему, за исключением числа параметров конструктора. И за счет отсутствия избыточной квалификации имени в SetDef. Ну так в чем же дело?
Of course, the code must be complete enough to compile and link.
Здравствуйте, Lorenzo_LAMAS, Вы писали:
L_L>Мой код практически идентичен твоему, за исключением числа параметров конструктора. И за счет отсутствия избыточной квалификации имени в SetDef. Ну так в чем же дело?
Я делал тоже самое, но разбивал на файлы.
Другой компилятор я использую дома. Я проверил, в "домашнем" варианте я использовал расширение файлов ".hpp", а на работе ".h"
Я сейчас изменил расширение хедера, на работе, с ".h" на ".hpp", все остальное оставил так как было (конечно еще в .cpp изменил имя подключаемого файла), все скомпилировалось. Я не понимаю как это связано.
Дома компилятор GCC 3.4
WB>Я делал тоже самое, но разбивал на файлы. WB>Другой компилятор я использую дома. Я проверил, в "домашнем" варианте я использовал расширение файлов ".hpp", а на работе ".h" WB>Я сейчас изменил расширение хедера, на работе, с ".h" на ".hpp", все остальное оставил так как было (конечно еще в .cpp изменил имя подключаемого файла), все скомпилировалось. Я не понимаю как это связано. WB>Дома компилятор GCC 3.4
.hpp или .h — роли не играет, главное, чтоб ты #include делал правильно. Если б проблема была с заголовком (его включением) дело вообще далеко не пошло бы — сказал бы, что не может включить и т.д.
Of course, the code must be complete enough to compile and link.
Здравствуйте, Lorenzo_LAMAS, Вы писали:
L_L>.hpp или .h — роли не играет, главное, чтоб ты #include делал правильно. Если б проблема была с заголовком (его включением) дело вообще далеко не пошло бы — сказал бы, что не может включить и т.д.
Ты знаешь, видимо я идиот и руки у меня кривые... Нормально все работает. Я сейчас еще раз руками все набрал. Только не пойму в чем причина была (столько времени мучился) , но компилятор получается не виноват.