C++(3 семестр)

Материал из Викиконспекты
Версия от 08:13, 9 января 2014; Kamigan4eg (обсуждение | вклад) (Наследование.)
Перейти к: навигация, поиск
Эта статья находится в разработке!

Содержание

1 часть

Наследование.

[1] Цель объектно-ориентированного программирования состоит в повторном использовании созданных вами классов, что экономит ваше время и силы. Если вы уже создали некоторый класс, то возможны ситуации, что новому классу нужны многие или даже все особенности уже существующего класса, и необходимо добавить один или несколько элементов данных или функций. В таких случаях C++ позволяет вам строить новый объект, используя характеристики уже существующего объекта. Другими словами, новый объект будет наследовать элементы существующего класса (называемого базовым классом). Когда вы строите новый класс из существующего, этот новый класс часто называется производным классом. В этом уроке впервые вводится наследование классов в C++ . К концу данного урока вы изучите следующие основные концепции:

Ели ваши программы используют наследование, то для порождения нового класса необходим базовый класс, т.е. новый класс наследует элементы базового класса. Для инициализации элементов производного класса ваша программа должна вызвать конструкторы базового и производного классов. Используя оператор точку, программы могут легко обращаться к элементам базового и производного классов. В дополнение к общим (public) (доступным всем) и частным (private) (доступным методам класса) элементам C++ предоставляет защищенные (protected) элементы, которые доступны базовому и производному классам. Для разрешения конфликта имен между элементами базового и производного классов ваша программа может использовать оператор глобального разрешения, указывая перед ним имя базового или производного класса. Наследование является фундаментальной концепцией объектно-ориентированного программирования. Выберите время для экспериментов с программами, представленными в этом уроке. И вы обнаружите, что реально наследование реализуется очень просто и может сохранить огромные усилия, затрачиваемые на программирование.

ПРОСТОЕ НАСЛЕДОВАНИЕ

Наследование представляет собой способность производного класса наследовать характеристики существующего базового класса. Например, предположим, что у вас есть базовый класс employee: class employee

{ public:

  employee(char *, char *, float); 
  void show_employee(void); 

private:

  char name[64]; 
  char position[64]; 
  float salary; 

}; Далее предположим, что вашей программе требуется класс manager, который добавляет следующие элементы данных в класс employee:

float annual_bonus; char company_car[64]; int stock_options;

В данном случае ваша программа может выбрать два варианта: во-первых, программа может создать новый класс manager, который дублирует многие элементы класса employee, или программа может породить класс типа manager из базового класса employee. Порождая класс manager из существующего класса employee, вы снижаете объем требуемого программирования и исключаете дублирование кода внутри вашей программы.

Для определения этого класса вы должны указать ключевое слово class, имя manager, следующее за ним двоеточие и имя employee, как показано ниже:

Производный класс //-----> class manager : public employee { <-------// Базовый класс

// Здесь определяются элементы };

Ключевое слово public, которое предваряет имя класса employee, указывает, что общие (public) элементы класса employee также являются общими и в классе manager. Например, следующие операторы порождают класс manager. class manager : public employee

{ public:

  manager(char *, char *, char *, float, float, int); 
  void show_manager(void); 

private:

  float annual_bonus; 
  char company_car[64]; 
  int stock_options; 

}; Когда вы порождаете класс из базового класса, частные элементы базового класса доступны производному классу только через интерфейсные функции базового класса. Таким образом, производный класс не может напрямую обратиться к частным элементам базового класса, используя оператор точку.

Следующая программа MGR_EMP.CPP иллюстрирует использование наследования в C++ , создавая класс manager из базового класса employee:

  1. include <iostream.h>
  1. include <string.h>

class employee

{ public:

  employee(char *, char *, float); 
  void show_employee(void); 

private:

  char name [ 64 ]; 
  char position[64]; 
  float salary; 

};

employee::employee(char *name, char *position,float salary)

{

  strcpy(employee::name, name); 
  strcpy(employee::position, position); 
  employee::salary = salary; 

}

void employee::show_employee(void)

{

  cout << "Имя: " << name << endl; 
  cout << "Должность: " << position << endl; 
  cout << "Оклад: $" << salary << endl; 

}

class manager : public employee

{ public:

  manager(char *, char *, char *, float, float, int); 
  void show_manager(void); 

private:

  float annual_bonus; 
  char company_car[64]; 
  int stock_options; 

};

manager::manager(char *name, char *position, char *company_car, float salary, float bonus, int stock_options) : employee(name, position, salary)

{

  strcpy(manager::company_car, company_car) ; 
  manager::annual_bonus = bonus ; 
  manager::stock_options = stock_options; 

}

void manager::show_manager(void)

{

  show_employee(); 
  cout << "Машина фирмы: " << company_car << endl; 
  cout << "Ежегодная премия: $" << annual_bonus << endl; 
  cout << "Фондовый опцион: " << stock_options << endl; 

}

void main(void)

{

  employee worker("Джон Дой", "Программист", 35000); 
  manager boss("Джейн Дой", "Вице-президент ", "Lexus", 50000.0, 5000, 1000); 
  worker.show_employee() ; 
  boss.show_manager(); 

} Как видите, программа определяет базовый класс employee, а затем определяет производный класс manager. Обратите внимание на конструктор manager. Когда вы порождаете класс из базового класса, конструктор производного класса должен вызвать конструктор базового класса. Чтобы вызвать конструктор базового класса, поместите двоеточие сразу же после конструктора производного класса, а затем укажите имя конструктора базового класса с требуемыми параметрами: manager::manager(char *name, char *position, char *company_car, float salary, float bonus, int stock_options) : employee(name, position, salary) //————————————— Конструктор базового класса

{ strcpy(manager::company_car, company_car); manager::annual_bonus = bonus; manager::stock_options = stock_options; } Также обратите внимание, что функция show_manager вызывает функцию show_employee, которая является элементом класса employee. Поскольку класс manager является производным класса employee, класс manager может обращаться к общим элементам класса employee, как если бы все эти элементы были определены внутри класса manager,

Представление о наследовании

Наследование представляет собой способность производного класса наследовать характеристики существующего базового класса. Простыми словами это означает, что, если у вас есть класс, чьи элементы данных или функции-элементы могут быть использованы новым классом, вы можете построить этот новый класс в терминах существующего (или базового) класса. Новый класс в свою очередь будет наследовать элементы (характеристики) существующего класса. Использование наследования для построения новых классов сэкономит вам значительное время и силы на программирование. Объектно-ориентированное программирование широко использует наследование, позволяя вашей программе строить сложные объекты из небольших легко управляемых объектов.

Второй пример

Предположим, например, что вы используете следующий базовый класс book внутри существующей программы: class book

{ public:

  book (char *, char *, int); 
  void show_book(void); 

private:

  char title[64]; 
  char author[б 4]; 
  int pages; 

}; Далее предположим, что программе требуется создать класс library_card, который будет добавлять следующие элементы данных в класс book: char catalog[64]; int checked_out; // 1, если проверена, иначе О Ваша программа может использовать наследование, чтобы породить класс library _card из класса book, как показано ниже: class library_card : public book

{ public:

  library_card(char *, char *, int, char *, int); 
  void show_card(void); 

private:

  char catalog[64] ; 
  int checked_out; 

}; Следующая программа BOOKCARD.CPP порождает класс library_card из клacca book:

  1. include <iostream.h>
  1. include <string.h>

class book

{ public:

  book(char *, char *, int); 
  void show_book(void); 

private:

  char title [64]; 
  char author[64]; 
  int pages; 

};

book::book(char •title, char *author, int pages)

{

  strcpy(book::title, title); 
  strcpy(book::author, author); 
  book::pages = pages; 

}

void book::show_book(void)

{

  cout << "Название: " << title << endl; 
  cout << "Автор: " << author << endl; 
  cout << "Страниц: " << pages << endl; 

}

class library_card : public book

{ public:

  library_card(char *, char *, int, char *, int); 
  void show_card(void) ; 

private:

  char catalog[64]; 
  int checked_out; 

};

library_card::library_card(char *title, char *author, int pages, char *catalog, int checked_out) : book(title, author, pages)

{

  strcpy(library_card::catalog, catalog) ; 
  library_card::checked_out = checked_out; 

}

void 1ibrary_card::show_card(void)

{

  show_book() ; 
  cout << "Каталог: " << catalog << endl; 
  if (checked_out) cout << "Статус: проверена" << endl; 
  else cout << "Статус: свободна" << endl; 

}

void main(void)

{

  library_card card( "Учимся программировать на языке C++", "Jamsa", 272, "101СРР", 1); 
  card.show_card(); 

} Как и ранее, обратите внимание, что конструктор library _card вызывает конструктор класса book для инициализации элементов класса book. Кроме того, обратите внимание на использование функции-элемента show_book класса book внутри функции show_card. Поскольку класс library_card наследует методы класса book, функция show_card может вызвать этот метод (show_book) без помощи оператора точки, как если бы этот метод был методом класса library _card.

ЧТО ТАКОЕ ЗАЩИЩЕННЫЕ ЭЛЕМЕНТЫ

При изучении определений базовых классов вы можете встретить элементы, объявленные как public, private и protected (общие, частные и защищенные). Как вы знаете, производный класс может обращаться к общим элементам базового класса, как будто они определены в производном классе. С другой стороны, производный класс не может обращаться к частным элементам базового класса напрямую. Вместо этого для обращения к таким элементам производный класс должен использовать интерфейсные функции. Защищенные элементы базового класса занимают промежуточное положение между частными и общими. Если элемент является защищенным, объекты производного класса могут обращаться к нему, как будто он является общим. Для оставшейся части вашей программы защищенные элементы являются как бы частными. Единственный способ, с помощью которого ваши программы могут обращаться к защищенным элементам, состоит в использовании интерфейсных функций. Следующее определение класса book использует метку protected, чтобы позволить классам, производным от класса book, обращаться к элементам title, author и pages напрямую, используя оператор точку: class book

{ public:

  book(char *, char *, int) ; 
  void show_book(void) ; 

protected:

  char title [64]; 
  char author[64]; 
  int pages; 

}; Если вы предполагаете, что через некоторое время вам придется породить новые классы из создаваемого сейчас класса, установите, должны ли будущие производные классы напрямую обращаться к определенным элементам создаваемого класса, и объявите такие элементы защищенными, а не частными.

Защищенные элементы обеспечивают доступ и защиту

Как вы уже знаете, программа не может обратиться напрямую к частным элементам класса. Для обращения к частным элементам программа должна использовать интерфейсные функции, которые управляют доступом к этим элементам. Как вы, вероятно, заметили, наследование упрощает программирование в том случае, если производные классы могут обращаться к элементам базового класса с помощью оператора точки. В таких случаях ваши программы могут использовать защищенные элементы класса. Производный класс может обращаться к защищенным элементам базового класса напрямую, используя оператор точку. Однако оставшаяся часть вашей программы может обращаться к защищенным элементам только с помощью интерфейсных функций этого класса. Таким образом, защищенные элементы класса находятся между общими (доступными всей программе) и частными (доступными только самому классу) элементами.

РАЗРЕШЕНИЕ КОНФЛИКТА ИМЕН

Если вы порождаете один класс из другого, возможны ситуации, когда имя элемента класса в производном классе является таким же, как имя элемента в базовом классе. Если возник такой конфликт, C++ всегда использует элементы производного класса внутри функций производного класса. Например, предположим, что классы book и library_card используют элемент price. В случае класса book элемент price соответствует продажной цене книги, например $22.95. В случае класса library'_card price может включать библиотечную скидку, например $18.50. Если в вашем исходном тексте не указано явно (с помощью оператора глобального разрешения), функции класса library_card будут использовать элементы производного класса {library_card). Если же функциям класса library_card необходимо обращаться к элементу price базового класса {book), они должны использовать имя класса book и оператор разрешения, например book::price. Предположим, что функции show_card необходимо вывести обе цены. Тогда она должна использовать следующие операторы: cout << "Библиотечная цена: $" << price << endl; cout << "Продажная цена: $" << book::price << endl;

Виртуальные функции, абстрактные функции.

Таблица виртуальных функций.

Виртуальный деструктор.

Множественное наследование.

Модификаторы доступа для наследования.

Виртуальное наследование.

Порядок инициализации при создании объекта.

Pure virtual function call, __declspec(novtable).

Ковариантность типа возвращаемого значения.

Приведение типов: static_cast, dynamic_cast, const_cast, reinterpret_cast;

RunTime Type Information.

Templates: функции и классы.

Полная (explicit) и частичная (partial) специализация.

Compile-time объекты.

Разница между специализацией и перегрузкой функции.

Template arguments deduction, partial ordering для специализаций.

Правила инстанциирования.

Двухфазный lookup имен: по не зависящим от параметров (independent) и зависимым (dependent) именам.

Ключевое слово typename при обращении к dependent именам.

Отложенный парсинг шаблонных функций компилятором Microsoft (Сорокин обещал давать на экзамене задание на эту тему).

Два подхода к инстанциированию: Cfront – инстанциирование по ошибкам линковки (перелинковка, export template), Borland – инстанциирование везде, но с пометкой inline. Явное инстанциирование, подавление истанциирования.

SFINAE (substitution failure is not an error).

Overload resolution, conversion sequence.

Tag dispatching.

Namespace.

Global namespace.

Директива using namespace.

Порядок поиска в пространствах имен.

Using декларация.

Анонимный namespace.

Argument-dependent lookup (Koenig lookup).

STL. Контейнеры, итераторы, алгоритмы.

Контейнеры: sequence, associative.

Итераторы: input, output, forward, bidirectional, random access.

Reverse итераторы.

Range.

Концепторы, функторы.

2 часть

Дерево развития С++

RAII. Scoped_ptr, shared_ptr, make_shared<T>, unique_ptr, make_unique<T>, linked_ptr. Inode.

Реальный код, генерируемый компилятором. Return value optimizations. Move, rvalue ссылки. Perfect forwarding. Reference collapsing rule.

Анонимные функции (lambda).

Type erasure. Any, any_cast: с использованием dynamic_cast, с использованием static поля.

Механизм сигналов и слотов на примере Boost. Понятие reentrancy.

SSE оптимизации, strict aliasing rule, restrict.

Обработка ошибок, исключения в деструкторе.

Кодировки семейства Unicode. Графема, глиф. Code point, combining character. Code unit. UTF-8, overlong sequence. UTF-16, surrogate pair.

Системы сборки. Make, make-files. Особенности make. Генераторы make-файлов. Cmake.

Нововведения С++11: auto, decltype, anonymous functions, static_assert, range_based for, non-static members initialization, delegating constructors, inherited constructors, variatic templates, extern template, raw string literals, template aliases, constexpr.

Нововведения С++14: template anonymous functions, ослабление условий на constexpr, расширение STL, tuple.