Настоящий "мгновенный" курс C++ в сжатой и всеобъемлющей форме представляет программирование на С++, прежде всего, для программистов, владеющих основами традиционного Си, и дает возможность совершить быстрый переход к С++.
При отсутствии опыта работы с Cи и С++, возможно, по-началу, могут показаться трудными новые концепции, обсуждаемые здесь, но разбор (и эксперименты) с примерами помогут прояснить картину. Прежде, чем начать работу, окружите себя уже имеющимися у Вас учебными пособиями и руководствами по С++. Возможно одно из них пригодится для более детального изучения С++.
Несмотря на то, что каждая тема с помощью примеров и подробных разъяснений раскрывает основные идеи языка, для желающих стать настоящими хакерами рекомендуется дополнительно проработать книги Страуструпа.
C++ - это Си с классами. Borland C++ - это версия фирмы Borland языка С++, разработанного Брайоном Страуструпом из фирмы AT&T, как расширение языка Си.
В целом язык С++ является надмножеством языка Си. Это означает, что программы Си можно компилировать в среде С++, однако компилировать программы С++ в среде Си, при наличии в них каких-либо специфических для С++ конструкций, нельзя. Некоторые ошибочные ситуации возникают из-за различий этих двух родственных языков. Так, одна и та же функция, дважды объявленная в Си с различными значениями аргументов, вызовет ошибку повторения имен. Однако, в С++ допустимо это или нет, зависит от других обстоятельств.
Главная возможность C++ - это классы, которые позволяют "упрятывать" функции и данные в отдельные конструкции. Отдельные конструкции называются объектами, а программирование с использованием объектов называют объектно-ориентированным программированием (ООП).
Почему бы не рассмотреть каков Турбо и Borland С++ немедленно?
Вот наша первая программа:
#include <stdio.h>
main()
{
int i, j;
i = 25; j = 17;
printf("Итого: %d\n", i+j);
return 0;
}
Сюрприз! В этой программе вы не увидели никаких отличий от программирования на Си. Чудесно! Это вселяет оптимизм, так как программы,
написанные на языке Си, можно компилировать в Турбо и Borland C++.
Зато Borland C++ имеет многочисленные расширения.
Указание комментариев
v
#include <iostream.h> // Новая библиотека потоков
// ввода-вывода
class sber_bank { // Новый способ создавать структуры
int memory_bank; // с данными и функциями.
public:
void add(int a, int b) { // Встроенная функция
memory_bank = a + b; // Сохранение данных в объекте
cout << "Итог: "; // Новый способ ввода-вывода
cout << memory_bank;
}; ^
}; Операция вывода потока
main()
{
sber_bank deep_thought; // Создание объекта и
deep_thought.add(25, 17); // обращение к нему
}
В общем случае семантика С++ намного более сложная по сравнению с Си, однако, это ведет к большей надежности и заставляет программистов быть более "дисциплинированными".
ООП в корне меняет положение, снабжая программные объекты, встроенными характеристиками, которые помогают справиться со все возрастающей сложностью разработки программного обеспечения.
Большинство новых возможностей Borland С++ - это поддержка ООП, с его важнейшими понятиями: инкапсуляцией, наследованием и полиморфизмом.
Компилятор Borland C++ имеют и другие - не объектно-ориентированные расширения, улучшающие программирование по сравнению с последней версией компилятора фирмы Borland языка Си - Турбо Си версии 2.0. Это - прототипы функций, встроенные функции, переопределяемые функции, функции-операции, предопределяемые аргументы, переменные-указатели, шаблоны, средства обработки исключительных ситуаций и другие.
Для информации ниже приводятся последние возможности, введенные в четвертой версии компилятора.
Borland С++ - это мощное профессиональное инструментальное средство для создания и поддержки приложений DOS, Windows Win32s и Windows NT на языках C++ и Си. Версия 4.0 Borland С++ включает множество последних достижений в области программирования:
Начнем с того, что объекты можно сравнить, отвлеченно, с объектами физического мира - компьютерами, автомобилями, электронными платами. Они обладают свойствами, такими, как, например, размер, производительность. Детали автомобиля, компьютера можно использовать многократно. Стандартные элементы позволяет разработчику сосредоточиться над стоящей перед ним задачей вместо того, чтобы заново изобретать средства для ее решения.
Наши объектно-ориентированные объекты - это объединение функций и данных (включая простые переменные и структуры) в виде независимой конструкции. Эти конструкции - строительные блоки вашей программы.
Ниже приведены простые примеры объектов:
Window: Bank:
int x, y, wd, ht; double savings;
void draw(); void deposit();
void hide(); double withdraw();
Объекты подобны миниатюрным программам. Они содержат и данные, и функции, которые в терминологии ООП называются методами. (В С++ функции класса называются функциями-элементами или, как их еще называют, - функциями-членами.)
Замечание: В других объектно-ориентированных языках классы часто называются объектами, функции элементы - методами, а элементы данных - полями.
Данные обычно специально "упрятываются" и могут принимать значения, от (для) функций-элементов объектов.
Объект Сбербанк
Sber_bank
< deposit << "Вклад $999"
$$$ < > Функции-элементы Передаваемые сообщения
Данные > Withdraw < > << "Снять $5555"
>> $$$ Деньги!
class sber_bank { // Объявление класса
public: // Упрятывание управления данными
double big_bucks; // Элемент данных
void deposit(double bucks); // Функция-элемент
double withdraw(double bucks); // Еще одна функция-элемент
};
В программировании на Си используются и переменные, и структуры
данных, и функции. Однако, они не очень удобно комплексируются в блоках. Borland C++ (и другие объектно-ориентированные языки) чудесно
помещают эти компоненты вместе, так что программисту приятно работать.
На схеме показан процесс создания классов:
Данные Комплексирование
классов
double big_bucks;
Функции > Bank
void deposit();
double withdraw();
>
Создание классов
Подобно тому, как структуры в Си используют для своей спецификации внутренние переменные, так классы в С++ используют для своей спецификации элементы-данных и функции-элементы. Для каждого задействованного, при написании программы класса, транслятор порождает соответствующие строки кода.
Класс sber_bank может стать основой по которой будут созданы
другие классы:
Класс Потомки класса sber_bank
v v v
sber_bank broke_bank, cold_cash_inc;
Объекты - на самом деле только переменные, созданные из структур. Так же как и в любой структуре, вы можете сохранять данные в
объекте.
class sber_bank {
Объект sber_bank public:
double big_bucks;
...
< Deposit << };
$$$ <-------------------------------
<<
Данные > Withdraw >>
Функции элементы
Доступ к данным объекта выполняется подобно обращению к элементу
структуры в языке Си через селектор '.'.
Например:
class sber_bank {
public:
double big_bucks;
...
};
sber_bank my_bank;
Объект Данные
v v
my_bank.big_bucks = 1000000.00;
^
Селектор
Банковская информация, как правило, доступна только ограниченному (разрешенному) кругу лиц. Делается это путем присвоения ей приватного уровня доступа. Например:
class sber_bank {
private: // Ключевое слово private
double big_bucks; // Приватный элемент данных
public: // Ключевое слово public
void deposit(double bucks); // Общая функция элемент
double withdraw(double bucks); // Общая функция элемент
};
Примечание: ключевое слово private необязательно. Любой элемент до первого элемента public (общий) автоматически приватный, если только не был определен иначе.
Приватные элементы классов недоступны снаружи. Например, следующее обращение к данным только что определенного класса будет неверным.
Нельзя, если big_bucks
sber_bank shaky_savings; является приватным (private)!
shaky_savings.big_bucks = 10000.00; <
Приватные элементы классов доступны только функциям-элементам
своего класса:
Селектор
Определяемый класс Функция элемент
v v v
void sber_bank::deposit(double bucks)
{
big_bucks += bucks;
} ^
Доступ к приватному элементу разрешен,
поскольку эта функция элемент принадлежит
классу sber_bank.
Проблема обращения к элементам не возникает, если они объявлены общими (public). В этом случае они открыты, даже функциям элементам других классов.
C++ обеспечивает удобные способы создания и удаления объектов,
через специальные процедуры. Процедуры создания объектов называются
конструкторами, а процедуры уничтожения - деструкторами. Конструкторы
автоматически вызываются при описании объекта, а деструкторы - при
выходе из блока, в котором этот объект был описан.
Если необходимые конструкторы или деструктор для класса не
описаны, то транслятор создает их сам.
Для того, чтобы понять важность конструкторов, сделаем маленькое
отступление. Общей проблемой для всех традиционных языков программирования является инициализация: прежде чем использовать структуру
данных, нужно выделить ей память и проинициализировать. Рассмотрим
задачу инициализации следующей структуры:
class rectangle { // Прямоугольник
public:
int wd, ht; // Ширина и высота
};
Неопытные программисты могут попробовать присвоить значения элементам wd и ht следующим образом:
rectangle this_rect // Создание нового объекта this_rect
this_rect.wd = 20; // с заданной шириной
this_rect.ht = 10; // и высотой
Ошибки не будет, но такой способ тесно связан с конкретным объектом, this_rect. Если понадобится проинициализировать несколько объектов типа rectangle, то операция присваивания потребуется использовать неоправданно часто. В данном случае естественным шагом является
создание функции инициализации, которая обобщает операции присваивания, работая с любым объектом типа rectangle, переданным ей в качестве аргумента:
void init_rect(rectangle* target, int wd, int ht)
{
target->wd = new_wd;
targrt->ht = new_ht;
}
Данная функция использует переданный ей указатель на объект типа rectangle для присваивания элементам объекта значений ее аргументов. С помощью операции -> указывается элемент, ссылающийся на элементы класса. Функция init_rect, разработанная специально для структуры rectangle, работает корректно.
Объектно-ориентированный подход требует возможности создания функции элемента, которая сможет инициализировать любой объект класса rectangle. Такой функцией является конструктор.
Итак, конструкторы используются для создания объекта. Благодаря
конструкторам разрабатываемые новые типы данных становятся такой неотъемлимой частью языка, как и встроенные типы. Конструктор определяет, каким образом новый объект типа класса будет создан, как будет
проводиться выделение памяти и инициализация объекта. Его определение
может включать инструкции для выделения памяти, присваивания значений
элементам, преобразование типов, и все что угодно, необходимое для
данного класса. При этом конструкторы наследуют имя своего класса:
class sber_bank { < Объявление класса
private:
double big_bucks;
public:
sber_bank(double bucks); < Конструктор
void deposit(double bucks);
double withdraw(double bucks);
};
Разберем порядок описания конструктора:
Селектор
Используемый класс Имя конструктора
v v v
sber_bank :: sber_bank(double bucks)
{
big_bucks = bucks; < Инициализация данных
}
Конструктор можно сделать встроенным:
class sber_bank {
private:
double big_bucks;
public:
// Встроенный конструктор
sber_bank(double bucks) { big_bucks = bucks; }
void deposit(double bucks);
double withdraw(double bucks);
};
Конструкторы могут вызываться неявно - через обращение к функции
элементу во время объявления объекта. Задавая объекту параметры, вы
сообщаете о их передаче непосредственно конструктором:
Имя Объект
класса
v v
sber_bank counterfeit_bank_of_AZ(1000000.00);
^ ^
Обусловлено вызовом конструктора
Конструкторы могут вызываться во время, когда выполняется обращение к объекту. Так, конструктор вызывается всякий раз, когда обращаются к функции элементу объекта:
void doit(void)
{
sber_bank telly(1000.0); < Здесь вызывается конструктор
...
Деструкторы, как следует из их названия, уничтожают объекты
класса, созданные перед этим конструктором, очищая значения и освобождая память. Деструкторы наследуют имена своих классов, с добавлением лидирующего знака тильда '~':
class sber_bank {
private:
double big_bucks;
public:
sber_bank(double bucks); < Конструктор
~sber_bank(void); < Деструктор
void deposit(double bucks);
double withdraw(double bucks);
};
В объявленном выше классе деструктор определяется так:
Селектор
Используемый класс Имя деструктора
v v v
sber_bank :: ~sber_bank(void)
{
printf("Машина останавливается \n");
}
Деструктор также можно объявлять встроенным:
class sber_bank {
private:
double big_bucks;
public:
// Встроенный конструктор
sber_bank(double bucks) { big_bucks = bucks; }
// Встроенный деструктор
~sber_bank(void) {printf("Машина останавливается ...\n");}
void deposit(double bucks);
double withdraw(double bucks);
};
Как и конструкторы, деструкторы могут вызываться явно (при помощи оператора С++ delete) или неявно - при выходе объекта из области
действия, например:
void doit(void)
{
sber_bank telly(1000.0); < Здесь вызывается конструктор
...
} < Деструктор вызывается, когда выполнение завершается
Если вы не определите деструктор для данного класса, то С++ генерирует деструктор по умолчанию.
Конструкторы и деструкторы тесно связаны с динамической памятью.
Вы должны использовать конструкторы для распределения памяти, а
деструкторы для возвращения занятой памяти. Например:
class string {
char *data; // Указатель на строку данных
int size; // Размер строки
public:
// Распределение памяти (см. "Динамическое использование
// свободной памяти")
string(int sz) { data = new char[size = sz]; }
// Возвращение занятой памяти
~string(void) { delete data; }
};
Как вы уже видели раньше, классы С++ могут содержать в качестве
своих элементов не только данные, но и функции элементы. Функция эле
мент - это функция, объявленная внутри определения класса и тесно
связанная с типом этого класса. В нашем примере - это операции которые выполняются в сберегательных банках. Наш объект sber_bank имеет
функции элементы deposite (вкладывать) и withdraw (снимать) деньги.
sber_bank
Объект сбербанк
< deposit << Вклад денег
$$$ < >
<
Данные > withdraw >> Снятие денег
Функции элементы
Наглядное представление функции элемента withdraw() приводится
ниже:
Селектор
Определяемый класс Имя функции элемента
Возвращаемый тип Аргумент
v v v v v
double sber_bank::withdraw(double bucks)
{
big_bucks -= bucks;
if (big_bucks < 0.0) {
printf("К сожалению, товарищ, у вас нет таких денег!\n");
big_bucks -= 10.0; // Штрафная санкция
}
return big_bucks;
}
Обратите особое внимание на использование операции селектор области действия '::' между определяемым классом и функцией элементом. Имя класса sber_bank используется для указания компилятору, к какому классу принадлежит withdraw (так как могут иметься и другие варианты withdraw, принадлежащие другим классам). При внутреннем определении селектор '::' не потребуется, так как и так ясно, что withdraw принадлежит классу sber_bank.
По аналогии, при обращении функций к элементам своего объекта,
селектор '.' лишний:
double sber_bank::withdraw(double bucks)
{
Обращение к данным своего объекта
v (не указывается принадлежность
big_bucks -= bucks; объекту - sber_bank.)
if (big_bucks < 0.0) {
printf("К сожалению, товарищ, у вас нет таких денег!\n");
big_bucks -= 10.0; // Штраф за перерасход
}
return big_bucks;
}
Поскольку функции элементы принадлежат объектам, то вы не можете
вызывать их напрямую - без указания принадлежности. Вызов может быть
выполнен только с некоторым значением аргумента:
Объект Имя функции элемента
v v
my_bank.deposit(paycheck);
^ ^ ^
Простой селектор
в обращении к задан- Вызов функции элемента
ному элементу струк-
туры
Общий вид синтаксиса вызова выглядит как:
имя_класса_объекта.имя_функции(список аргументов).
Как на самом деле передаются значения в C++? Они передаются с
вызовом функции. Но это сможет сделать не любая функция, а только
функция которая встроена в объект. Этот тип функций называется функцией элементом или методом.
Тип объекта Объект
v v
sber_bank my_bank;
double paycheck = 100.0; < Старый стиль передачи
double party_money; значения по переменной, а
не по ссылке
Объект Метод (выбранное сообщение)
v v
my_bank.deposit(paycheck); Значение
v
party_money = my_bank.withdraw(500.00);
Классы являются сердцевиной ООП, поскольку именно объекты встроены в классы, а не наоборот. Для программирующих на Си классы являются расширениями структур языка Си, дополненные "механизмами" скрытия данных, функциями элементами и наследованием.
Ниже показана взаимосвязь старых структур Си, новых структур C++
и классов:
Структура Си Структура C++ Класс C++
typedef struct { struct rect { class rect {
int wd, ht; int wd, ht; public:
} rect; }; int wd, ht;
};
В отличии от структур Си, структуры C++ фактически являются типами определяемыми пользователем, без использования ключевого слова
typedef. На помощь программистам добавлено новое для Си ключевое слово: class. Естественно, что класс, имеющий все элементы типа public
без каких-либо функций, является обычной структурой Си.
Ниже приводится типичный пример класса:
class circle { < Объявление класса
private: < Следующий элемент приватный
double radius; < Приватный элемент
public: < Следующие элементы общие
void init(double r); < Функция элемент
double area(void); < Еще одна функция элемент
};
Назад | Содержание | Вперед