1.4 Синтаксис
1.4.1 Полезные обороты
long с = (long)a; - традиционная запись;
long с = long(a); - функциональная запись;
- Декларация нескольких переменных одного типа:
int x, // x
y, // y
z; // z
- Варианты использования параметров функций:
- для меняющихся аргументов необходимо использовать ссылки или указатели (ссылки на массив);
int test(int *ptr, int &alias, int[] &mass );
- для неизменяемых аргументов использовать прямую передачу по значению ;
int test(int value);
- для больших неизменяемых аргументов использовать константные ссылки или указатели на константные данные;
int test(const int &value, const int *ptr);
- Предотвращение многократного включения заголовочных файлов:
#ifndef TEST_H
#define TEST_H
<code>
#endif
- Комментирование/исключение больших кусков кода:
- Использование значений параметров функций по умолчанию:
void show_values(int one=1, int two=2, int three=3);
show_value();
show_value(23);
- Объявление переменных по месту:
for (int count = 0; count < 10;count++)
- Функции переменного числа параметров:
void text(parm x,char *fmt, ...)
{
char str[100];
va_start (argptr,fmt);
vsprintf(str,fmt,argptr);
va_end(argptr);
printf(str);
}
text(54,"hello %s","world");
- Указание способа(языка) для которого должна компилироваться функция:
extern "C" func(); // В стиле "C"
extern "C++" func(): // В стиле "C++"
extern "C++"
{
int func(void);
}
1.4.2 Указатели
Особенностью языка является возможность доступа к переменной не только по имени но и с использованием механизма указателей. Для этого в языке предусмотрены символы: "&" и "*".
Символ "*" используется для индикации переменной (*ptr), которая расположена в памяти по адресу на который указывает одноимённая переменная без звёздочки. Символ "&" используется как для определения адреса ячейки памяти переменной, так и для определения адреса указателя на переменную.
- Назначение адреса указателя:
int *ptr = (int *)0x0010; //при инициализации
ptr = (int *)0x0010; //в программе
- Присвоение значения непосредственно переменной на которую указывает указатель:
- Родовой указатель в основан на использовании указателя типа (void *). Ключевое слово void говорит об отсутствии данных о размере объекта в памяти. Во всех случаях использования указателя описанного как void*, необходимо выполнять операцию явного приведения типа указателя:
unsigned long block = 0xffeeddccL;
void *ptr = █
unsigned char = *(unsigned char *)ptr;
long int four_bytes = *(long int *)ptr;
- Определение адреса указателя:
int data = 5;
int *ptr = &data; // ptr[0]==5;
int **ptr1 = &ptr; // ptr1[0][0]==5;
int ***ptr2 = &ptr1; // ptr2[0][0][0]==5;
- Возврат указателя функцией:
bool (* compare)(int, int);
- Указатели на члены класса (.* и ->*)
class Test
{
public:
void funct() { cout << "Функция\n"; }
int value;
};
Test t;
Test *tPtr = &t;
void (Test::*memPtr)() = &Test::funct;
int Test::*vPtr = &Test::value;
(tPtr->*memPtr)(); //косвенный вызов функции
cout << (*tPtr).*vPtr << endl;
1.4.3 Ссылки (C++)
Ссылка являются псевдонимом (алиасом) от переменной на которую она ссылается. При изменении ссылаемой переменной изменяется ссылка. В основном ссылки используются при описании параметров функций и указывают что переменная может меняться.
int &test(int &x);
int data = 5;
int &al_data = data; // al_data == 5;
al_data = 10; // data == 10;
data = 7; // al_data == 7;
1.4.4 Массивы
Как и в других языках, поддерживает массивы которые тесно переплетаются с указателями. Элементы массива имеют один и тот же тип и расположены в памяти друг за другом. Имя массива также можно воспринимать как указатель на начало массива.
В отличие от других языков в отсутствует специальный строковый тип. Вместо него строковые литералы представляются как одномерный массив элементов типа char оканчивающегося символом "0".
- Явное указание числа элементов массива и списка начальных значений:
char array[] = {'A','B','C','D',0};
char array[] = "ABCD";
char array[5] = {'A','B','C','D',0};
char *string = "ABCD";
string = "ABCD";
- Обращение к элементам массива с помощью указателя:
*(array+i);
array[i][j];
*(array[i]+j);
*(*(array+i)+j);
matrix[2] == &matrix[2][10];
long (* matrix1)[3][2][4];
matrix1 = new long[3][2][4];
char *messages[20] == char messages[20][80]);
char string[][80]=
{
"Первая строка",
"Вторая строка",
"Следующая строка"
};
int m[][3] = { {00}, {10, 11}, {20, 21, 22,} };
char *Names[]= { "Aleksey", "Vladislav", "Vitaly" };
- Массив указателей на функцию:
int (* fcmp[5])(int) =
{cmp_name, cmp_title, cmp_year, cmp_price, cmp_totaly};
void (* func[3])(int); //определение
(* func[choice])(choice); //вызов
1.4.5 Перегрузка функций
В языке C++ разрешается иметь множество функций с одним и тем же именем, но отличающиеся типами параметров или их количеством:
int sum(int *array, int element) { }
float sum(float *array, int element) { }
1.4.6 Перегрузка операций
Перегрузкой операция является процедура расширения функций существующих операция для новых типов x(объектов). Операции допускающие перегрузку указанны в табл.4 . При перегрузке операций их старшинство и ассоциативность не изменяется.
- Унарная операция (префиксная).
Операция: !S
Вызывает: S.operator!() или operator!(S)
Объявляется: bool operator!() const; или friend bool operator!(const String &);
- Унарная операция (постфиксная).
Операция: d1++
Вызывает: d1.operator(0) или operator(d1,0)
Объявляется: Date::operator(int); или friend Date::operator(Date &,int);
Операция: y+=z
Вызывает: y.operator+=(z) или operator+=(y,z)
Объявляется: const String &operator+=(const String &); или friend const String &operator+=(String &, const String &);
Операция: string(2,2);
Вызывает: string.operator()(2,2);
Объявляется: String operator()(int,int);
Операция: (char *)S;
Вызывает: S.operator char*()
Объявляется: String operator char*(); или String(char *); (конструктор неявного преобразования).
Операция: Class1 *cls = new Class1; или Class1 *cls = new ("class") Class1;
Вызывает: Class1 *cls = Class2.operator new(sizeof(Class1)); или Class1 *cls = Class2.operator new("class", sizeof(Class1));
Объявляется: void* Class2::operator new(size_t size); или void* Class2::operator new(string modul, size_t size);
Операция: delete cls;
Вызывает: Class2.operator delete(cls);
Объявляется: void* Class2::operator delete(void *addr);
Таблица 4.
Операции допускающие перегрузку
+ | - | * | / | % | ^ | & | | |
~ | ! | = | < | > | += | -= | *= |
/= | %= | ^= | &= | |= | << | >> | >>= |
<<= | == | != | <= | >= | && | || | ++ |
-- | ->* | , | -> | [] | () | new | delete |
new[] | delete[] |
1.4.7 Шаблоны
Шаблоны определяются с помощью ключевого слова
template и предназначены для определения функций и классов способных работать с различными типами входных и выходных параметров. Шаблоны и наследование связаны следующим образом:
- шаблон класса может быть производным от шаблонного класса;
- шаблон класса может являться производным от нешаблонного класса;
- шаблон класса может быть производным от шаблона класса;
- нешаблонный класс может быть производным от шаблона класса.
Шаблонные классы:
template <class Templ>
class Tree
{
public:
Tree( const Templ& n );
insertN(const Templ &);
}
template <class Templ>
Tree<Templ>::Tree(const Templ& n) { };
Tree<int> NewTree(23);
Tree<float> NewTree(56.8);
Шаблонные функции:
template <class T> // или template <typname T>;
T max(T val1, T val2, T val3)
{
T max = val1;
if(val2 > max) max=val2;
if(val3 > max) max=val3;
return max;
}
int rez = max(1,10,3);
float rez = max(0.5,9.99,6.78);
Шаблоны и друзья:
friend void f1(); //друг любого класса
friend void f2(x<T> &); //друг конкретного класса
friend void A::f4(); //друг любого класса
friend void C<T>::f5(x<T> &); //друг конкретного класса
friend class Y; //класс Y дружественен любому классу
friend class Z<T>; //класс Y дружественен конкретному классу
1.4.8 Обработка исключительных ситуаций
В языке C++, добавлен мощный аппарат обработки исключительных ситуаций. Этот аппарат позволяет отлавливать как все типы исключений, так и конкретно взятый тип исключений. Так если записать catch(...), то будут отлавливаться все типы исключений. Кроме того обработка исключительных ситуаций оказываться вынесенной из "основной линии" выполнения программы. Для генерации повторных исключений в catch опускаться использование (throw) без параметров. Если происходит глубоковложенное исключение, то выполняется "раскрутка" стека для возвращения до ближайшего catch Обработка исключительных ситуаций описывается следующим образом:
try //начало блока испытания
{
if() throw MyType(); // Принудительная генерация
} // (точка генерации)
catch(MyType &Mt) {...}; // Отлов и обработка исключений My Type
catch(...){...}; // Отлов и обработка всех остальных исключений
Для ограничения круга генерируемых исключений функцией, можно указать спецификацию исключений для функции:
int g(double h) throw(a,b,c) //может генер. a,b,c и
//unexpected
int g(double h) throw() //не может генерировать
//(кроме unexpected)
int g(double h) //может генерировать все
Стандартные обработчики исключений:
- bad_alloc (exception) -- ошибка выделения памяти с помощью new;
- bad_cast (exception) -- генерируется dynamic_cast;
- bad_exception (exception) -- неожиданное исключение генерируется в случае неожиданного исключения при включении std::bad_exception в throw-список функции;
- bad_typeid (exception) -- генерируется typeid;
- invalid_argument (exception) -- функции передан недопустимый аргумент;
- logic_error (exception) -- исключения в логических операциях;
- length_error (exception) -- длина более максимально допустимой;
- out_of_range (exception) -- аргумент вне допустимого диапазона;
- runtime_error (exception) -- Ошибка в программе;
- overflow_error (runtime_error) -- математическая ошибка переполнения сверху;
- underflow_error (runtime_error) -- математическая ошибка переполнения снизу;
Чтоб избежать утечек памяти, обусловленных забыванием вызова delete после new, можно использовать шаблон auto_ptr, который будет автоматически разрушаться.
На базе класса exception, можно генерировать собственные исключения.