//файл HEADER.HPP class MyClass {/* ... */}; // содержит описание некоторого ... // класса //файл DESKTOP.HPP #include "HEADER.HPP" // подключает описанный ранее файл class DeskTop {/* ... */}; //и определяет собственные типы ... //файл DRIVER.CPP #include "HERADER.HPP" #include "DESKTOP.HPP" class Driver {/* ... */}; ...
При попытке откомпилировать файл DRIVER.CPP во второй строке компилятор выдаст ошибку, поскольку файл HEADER.HPP включен дважды и, соответственно, типы переменных, описанные в этом файле, будут описаны дважды.
Для предотвращения таких случаев можно организовать достаточно простую "ловушку", использую директиву условной компиляции. Измененный файл HEADER.HPP выглядит следующим образом.
//файл HEADER.HPP #ifndef __HEADER_HPP //если перем. __HEADER_HPP не описана, #define __HEADER_HPP //то определить ее пустым значением class MyClass {/* ... */} // определение требуемых типов #endif // конец директивы
Лучше всего любые заголовочные файлы в приложении организовывать приведенным выше способом.
Отметим, что громоздкое имя переменной __HEADER_HPP
создается по имени файла и такое имя практически позволяет избежать ошибки, связанной с совпадением имен.
... for (int i=0; i<10; i++) {//объявление перем. i снаружи /* ... */ //блока, содержащего оператор for } ... { int i = GetValue(); //объявление другой переменной i char* cp = NextString(); ... }
Время "жизни" любой переменной в С++ определяется блоком {...}, в котором она объявлена, поэтому пeременной i, объявленной в цикле for, не существует при выходе из блока, содержащего оператор цикла. Целесообразно объявлять переменную в том месте, где она впервые понадобилась.
const int TRUE = 1; //определение типизованных констант const int FALSE = 0; const float ALT = 1.5; ... while (TRUE) { ... int a = FALSE; //использование констант с контролем типа float b = TRUE;//нет предупреждения о различных типах int i = ALT; //предупреждение о различных типах ... }
Поскольку константные объекты в С++ по умолчанию не доступны из других модулей, при необходимости следует использовать ключевое слово extern. Пример.
extern const int FALSE = 0; extern const int TRUE = 1;
void func(char*, int, float*);//верное объявление функции
Более того, компилятор языка С++ контролирует использование описанных переменных и входных параметров функции. Если входной параметр функции не используется в ее теле, то для подавления соответствующего предупреждения можно оставить только описание типа этого параметра, например
int func (int a, int /* b */) { return (a-1); }
Можно описать функцию с параметрами по умолчанию, например
int f(int a = 0) { return a+1;}
Вызов такой функции без параметров, означает, что входной параметр равен нулю, а результат, возвращенный функцией равен единице.
Переменные ссылки - нововведение в С++. Переменные ссылки также содержат адрес объекта, но подчиняются другим правилам. Ссылки необходимо инициализировать при объявлении. Рассмотрим пример.
int a = 3; //объявление и инициализация переменной int& ia = a;//объявление и инициализация ссылки на //переменную
Переменная ссылка ссылается на объект, ее можно использовать так, как будто она является альтернативным именем объекта.
int& i = 3; int j; if (i == 3) { i--; j = i; } i = strlen("title");
Нельзя объявить ссылку типа void.
void& a = 3; //неверно!
Значение ссылки во время ее существования можно изменить, если только она не объявлена как константа.
const int& number = 10; ... number = 5; // ошибка! number = 10; // ошибка! ...
int i = 10; int* ip = new int[i]; // выделение памяти //для массива из 10 целых чисел
Объем выделяемой памяти должен быть известен во время выполнения программы. Для освобождения памяти используется оператор delete, а для освобождения памяти распределенной под массив используется delete[], например
char* cp = new char[100];//выделение памяти ... delete[] cp;//освобождение памяти
С++ является языком объектно-ориентированного программирования (ООП). Объект - абстрактная сущность, наделенная характеристиками объектов реального мира. Как и любой другой язык ООП, С++ использует три основные идеи ООП - инкапсуляцию, наследование и полиморфизм.
Инкапсуляция - сведение кода и данных воедино в одном объекте, получившим название класс.
Наследование - наличие в языке ООП механизма, позволяющего объектам класса наследовать характеристики более простых и общих типов. Наследование обеспечивает как требуемый уровень общности, так и необходимую специализацию.
Полиморфизм - дословный перевод с греческого "много форм". В С++ полиморфизм реализуется с помощью виртуальных функций, которые позволяют в рамках всей иерархии классов иметь несколько версий одной и той же функции. Решение о том, какая именно версия должна выполняться в данный момент, определяется на этапе выполнения программы и носит название позднего связывания.
Класс (class) - это тип, определяемый пользователем, включающий в себя данные и функции, называемые методами или функциями-членами класса.
Данные класса - это то, что класс знает.
Функции-члены (методы) класса - это то, что класс делает.
Таким образом, определение типа задаваемого пользователем (class) содержит спецификацию данных, требующихся для представления объекта этого типа, и набор операций (функций) для работы с подобными объектами.
class TCounter { long count; // данные класса public: long GetValue(); //функции-члены класса void SetValue(long); };
Определение класса начинается с ключевого слова class за которым следует имя класса. Имя класса в BC 4.5 может иметь до 32 символов, причем различаются строчные и прописные буквы. Открывающая и закрывающая фигурные скобки определяют тело класса, в которое включено описание данных и функций класса. Заканчивается описание класса символом ;. Класс имеет столько переменных (данных), сколько необходимо. Переменные могут быть любого типа, включая другие классы, указатели на классы и указатели на динамически распределяемые объекты. Переменные объявленные внутри класса имеют область видимости класса, т.е. от точки объявления переменной до конца класса.
// установить значение счетчика void TCounter::SetValue(long val) { count = val; } //получить значение счетчика long TCounter::GetValue() { return count; }
В приведенной реализации запись TCounter:: сообщает компилятору, что реализация функций принадлежит классу TCounter. Символ :: является операцией определения области действия.
void main(void) { TCounter cnt; //создание объекта cnt типа TCounter cnt.SetValue(10); //вызов метода для инициализации //определение и инициализация указателя на объект TCounter* p = &cnt; int i = p->GetValue();//использование указателя //определение ссылки TCounter& Rp = &cnt; i = Rp.GetValue(); //использование ссылки //Определение массива указателей TCounter* m[10]; //Создание и инициализация объектов for (int k = 0; k < 10; k++) { m[k] = new TCounter; m[k]->SetValue(0); } //Освобождение памяти for (i = 0; i < 10; i++) delete m[i]; }
class Example { int x1; // частные по умолчанию int f1(void); protected: int x2; // защищенные int f2(void); private: int x3; // опять частные int f3(void); public: int x4; // общедоступные inf f4(void); };
class TPrivateClass { int value; int GetValue(void); }; int TPrivateClass::GetValue(void) { return value; //доступ разрешен } void main(void) { TPrivateClass cl; //создание объекта int i = cl.value; //ошибка! Нет доступа i = cl.GetValue();//ошибка! Нет доступа }
class A { protected: int val; }; class B: public A { //наследуется от A public: void fb(); }; void B::fb() { val = 0; } //доступ разрешен class C:public B { //наследуется от B public: void fc(); }; void C::fc() {val = 10;} //доступ разрешен
В данном примере приведена иерархия классов A->B->C. Свойство защищенности распространяется вниз по иерархии до тех пор пока производный класс объявляет свой базовый общедоступным (public). При этом любые функции-члены в классах C и B имеют доступ к члену данных val базового класса. Если функция-член производного класса в качестве входного параметра имеет указатель или ссылку на объект базового класса, то правила становятся другими. Модифицируем класс C следующим образом.
class C:public B { public: void fc(A&); //Входной параметр ссылка на базовый класс }; void C::fc(A& a) { val = 10; //доступ разрешен a.val = 10; //ошибка! нарушение прав доступа }