//файл 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; //ошибка! нарушение прав доступа
}