40гг первые цифровые компьютеры программирование путем коммутации проводов

Вид материалаДокументы
Подобный материал:
1   ...   4   5   6   7   8   9   10   11   12

date *pd = new date;

pd->set(2006,1,1);

pd->print();

delete pd;


Наследование и конструкторы
  • Конструкторы не наследуются, поэтому производный класс должен иметь собственные конструкторы. Порядок вызова конструкторов определяется приведенными ниже правилами.
  • Если в конструкторе производного класса явный вызов конструктора базового класса отсутствует, автоматически вызывается конструктор базового класса по умолчанию (то есть тот, который можно вызвать без параметров).
  • Для иерархии, состоящей из нескольких уровней, конструкторы базовых классов вызываются начиная с самого верхнего уровня. После этого выполняются конструкторы тех элементов класса, которые являются объектами, порядке их объявления в классе, а затем исполняется конструктор класса.

Пример вызовов конструкторов




class A {

public:

A();

X x;



};

class B : public A {

public:

B();

Y y;



};

class C : public B {

public:

C();

Z z;



};

class D : public B {

public:

D();



};

class X {

public:

X();



};

class Y {

public:

Y();



};

class Z {

public:

Z();



};


При создании экземпляра класса С вызов конструкторов будет происходить в следующей последовательности:




С с;

или

С *с = new c;


Наследование деструкторов
  • Деструкторы не наследуются, и если программист не описал в производном классе деструктор, он формируется по умолчанию и вызывает деструкторы всех базовых классов.
  • В отличие от конструкторов, при написании деструктора производного класса в нем не требуется явно вызывать деструкторы базовых классов, поскольку это будет сделано автоматически.
  • Для иерархии классов, состоящей из нескольких уровней, деструкторы вызываются в порядке, строго обратном вызову конструкторов: сначала вызывается деструктор класса, затем — деструкторы элементов класса, а потом деструктор базового класса.

Последовательность вызовов деструкторов при вызове


delete c;
  • Если в классе С определен деструктор, то выполняется его тело.
  • Выполняется деструктор для объекта z.
  • Выполняется деструктор для базового класса B
  • Если в классе B определен деструктор, то выполняется его тело.
  • Выполняется деструктор для объекта Y.
  • Выполняется деструктор для базового класса A.
  • Если в классе A определен деструктор, то выполняется его тело.
  • Выполняется деструктор для объекта X.


Операция присваивания
  • Операция присваивания не наследуется.
  • Требуется явное определение операции присваивания в производном классе.


Пример использования операции присваивания (1)

class man{

private:

char *fio;



public:

const man & operator=(man &m){

if(fio != NULL) delete fio;

if(m.fio != NULL){

fio = new char[strlen(m.fio)+1];

strcpy(fio,m.fio);

}

else

fio = NULL;

return *this;

}

};

Пример использования операции присваивания (2)

class business_man: public man{

private:

unsigned long int many;



public:

const business_man & operator=(business_man &m){

many = m.many;

man::operator=(m);

return *this;

}

};


Полиморфизм и виртуальные методы
  • Работа с объектами чаще всего производится через указатели.
  • Благодаря свойству полиморфизма указателю на базовый класс можно присвоить значение адреса объекта любого производного класса.
  • Например:

class A {};

class B : public A{};

A *pointer;

pointer = new B;
  • В каждом из классов опишем по методу с одинаковым названием.

class A {

public:

void metod1();



};

class B : public A{

public:

void metod1();



};

Вызов методов объекта происходит в соответствии с типом указателя, а не фактическим типом объекта, на который он ссылается, поэтому при выполнении оператора, например,

A *pointer = new B;

pointer->metod1();

будет вызван метод класса A, а не класса B, поскольку ссылки на методы разрешаются во время компоновки программы.

Этот процесс называется ранним связыванием.

Чтобы вызвать метод класса B, можно использовать явное преобразование типа указателя:

((B *)pointer)->metod1();

Это не всегда возможно, поскольку в разное время указатель может ссылаться на объекты разных классов иерархии, и во время компиляции программы конкретный класс может быть неизвестен.

Например:
  • Функция, параметром которой является указатель на объект базового класса. На его место во время выполнения программы может быть передан указатель на любой производный класс.
  • Связный список указателей на различные объекты иерархии, с которым требуется работать единообразно.
  • В C++ реализован механизм позднего связывания, когда разрешение ссылок на метод происходит на этапе выполнения программы в зависимости от конкретного типа объекта, вызвавшего метод.
  • Этот механизм реализован с помощью виртуальных методов.
  • Виртуальным называется метод, ссылка на который разрешается на этапе выполнения программы.

Виртуальные методы
  • Для определения виртуального метода используется спецификатор virtual, например:

class A {

public:

virtual void metod1();



};

class B : public A{

public:

virtual void metod1();



};

Правила описания и использования виртуальных методов
  • Если в базовом классе метод определен как виртуальный, метод, определенный в производном классе с тем же именем и набором параметров, автоматически становится виртуальным, а с отличающимся набором параметров — обычным.

class A {

public:

virtual void metod1();



};

class B : public A{

public:

void metod1(); // виртуальный метод

void metod1(int a); // обычный метод

…};
  • Виртуальные методы наследуются, то есть переопределять их в производном классе требуется только при необходимости задать отличающиеся действия. Права доступа при переопределении изменить нельзя.

class A {

public:

virtual void metod1();



};

class B : public A{

public:



};


A *pointer = new B;

pointer->metod1(); // допустимая операция будет вызван

// метод класса A
  • Если виртуальный метод переопределен в производном классе, объекты этого класса могут получить доступ к методу базового класса с помощью операции доступа к области видимости.

class A {

public:

virtual void metod1();



};

class B : public A{

public:

void metod1(){



A::metod1();



}



};

A *pointer = new B;

pointer->metod1();
  • Если в базовом классе метод определен как виртуальный, метод, определенный в производном классе с тем же именем и набором параметров, автоматически становится виртуальным, а с отличающимся набором параметров — обычным.
  • Виртуальные методы наследуются, то есть переопределять их в производном классе требуется только при необходимости задать отличающиеся действия. Права доступа при переопределении изменить нельзя.
  • Если виртуальный метод переопределен в производном классе, объекты этого класса могут получить доступ к методу базового класса с помощью операции доступа к области видимости.
  • Виртуальный метод не может объявляться с модификатором statiс.


Абстрактные классы и чисто виртуальные методы
  • При описании класса можно описать виртуальный метод у которого нет реализации в данном классе. Для этого используется специальный синтаксис.

class A {

public:

virtual void metod1() = 0;



};
  • Такой метод называется чисто виртуальным.
  • Класс, содержащий хотя бы один чисто виртуальный метод, называется абстрактным.
  • Абстрактные классы предназначены для представления общих понятий, которые предполагается конкретизировать в производных классах.
  • Абстрактный класс может использоваться только в качестве базового для других классов — объекты абстрактного класса создавать нельзя, поскольку прямой или косвенный вызов чисто виртуального метода приводит к ошибке при выполнении.



Правила использования абстрактных классов
  • Абстрактный класс нельзя использовать при явном приведении типов, для описания типа параметра и типа возвращаемого функцией значения;
  • допускается объявлять указатели и ссылки на абстрактный класс, если при инициализации не требуется создавать временный объект;
  • если класс, производный от абстрактного, не определяет все чисто виртуальные функции, он также является абстрактным.
  • Можно создать функцию, параметром которой является указатель на абстрактный класс. На место этого параметра при выполнении программы может передаваться указатель на объект любого производного класса.


Пример использования абстрактных классов

class Figure {

public:

virtual void draw() = 0;



};

class Triangle : public Figure{

public:

virtual void draw();



};

class Square : public Figure{

public:

virtual void draw();



};


Figure *mas[2];

mas[0] = new Triangle();

mas[1] = new Square();

for(int i=0;i<2;i++)

mas[i]->draw();


Множественное наследование
  • Множественное наследование означает, что класс имеет несколько базовых классов.
  • Множественное наследование применяется для того, чтобы обеспечить производный класс свойствами двух или более базовых.
  • Чаще всего один из этих классов является основным, а другие обеспечивают некоторые дополнительные свойства, поэтому они называются классами подмешивания.



Иерархия классов при множественном наследовании




Синтаксис множественного наследования

class имя:

[private|protected|public] базовый_класс1,

[private|protected|public] базовый_класс2,



{

тело класса

};

Пример множественного наследования

class clX {

public:

clX();

int ValueX;



};

class clY {

public:

clY();

bool ValueY;



};

class clZ: public clX,

public clY{

public:

clZ();

double ValueZ;



};



clZ *z = new clZ;

z->ValueX = 10;

z->ValueY = true;

z->ValueZ = 5.7;



delete z;


Проблемы множественного наследования
  • Если в базовых классах есть одноименные элементы, при этом может произойти конфликт идентификаторов.
  • Если у базовых классов есть общий предок, это приведет к тому, что производный от этих базовых класс унаследует два экземпляра полей предка, что чаще всего является нежелательным (ромбовидное наследование).

Пример устранения конфликта идентификаторов

class clX {

public:

clX();

int ValueQ;



};

class clY {

public:

clY();

int ValueQ;



};

class clZ: public clX,

public clY{

public:

clZ();



};




clZ *z = new clZ;

z->clX::ValueQ = 10;

z->clY::ValueQ = 20;



delete z;

Конфликт двойного наследования общего предка




Устранение конфликта двойного наследования предка

class man {

public:

man();

char *fio;



};

class Listener:

virtual public man {

public:

Listener();

int group;



};

class AutoOwner:

virtual public man{

public:

AutoOwner();

char GosN[9];



};

class AutoListener:

public Listener,

public AutoOwner

{

public:

AutoListener();

char propuskN[10];



};


Шаблоны классов
  • Шаблоны классов предназначаются для отделения алгоритмов от конкретных типов данных и позволяют создавать параметризованные классы.
  • Параметризованный класс создает семейство родственных классов, которые можно применять к любому типу данных, передаваемому в качестве параметра.
  • Наиболее широкое применение шаблоны находят при создании контейнерных классов.
  • Контейнерным называется класс, который предназначен для хранения каким-либо образом организованных данных и работы с ними.
  • Преимущество использования шаблонов состоит в том, что как только алгоритм работы с данными определен и отлажен, он может применяться к любым типам данных без переписывания кода.



Односвязный линейный список



Класс для контейнера «стек целых чисел»

class Stack{

private:

struct DataElement{

int Value;

struct DataElement *next;

};

DataElement *first;

public:

Stack(){ first = NULL;}

~Stack(){ while(first != NULL) Pop();}

void Push(int value){

DataElement *d = new DataElement();

d->Value = value;

d->next = first;

first = d;

}

int Pop(){

if(first == NULL) return 0;

DataElement *d = first;

first = first->next;

int tmp = d->value;

delete d;

return tmp;

}

};

Использование контейнера «стек целых чисел»



Stack *st = new Stack();

st-> Push(10);

st-> Push(15);

st-> Push(20);

int tmp = st->Pop();

tmp = st->Pop();



delete st;


Синтаксис определения шаблона класса

template <описание_параметров_шаблона> определение_класса;
  • Параметры шаблона перечисляются через запятую.
  • В качестве параметров могут использоваться типы, шаблоны и переменные.
  • Типы могут быть как стандартными, так и определенными пользователем.
  • Для их описания используется ключевое слово class.
  • Внутри шаблона параметр типа может применяться в любом месте, где допустимо использовать спецификацию типа.


Шаблон для контейнера «Стек»

template class Stack{

private:

struct DataElement{

T Value;

struct DataElement *next;

};

DataElement *first;

public:

Stack(){ first = NULL;}

~Stack(){ while(first != NULL) Pop();}

void Push(T value){

DataElement *d = new DataElement();

d->Value = value;

d->next = first;

first = d;

}

T Pop(){

if(first == NULL) return 0;

DataElement *d = first;

first = first->next;

T tmp = d->value;

delete d;

return tmp;

}

};


Использование шаблона «стек»

Stack *st = new Stack();

st-> Push(10);

st-> Push(15);

int tmp = st->Pop();

tmp = st->Pop ();



delete st;



Stack *st = new Stack();

st-> Push(10.6);

st-> Push(15.7);

double tmp = st->Pop ();

tmp = st->Pop ();



delete st;



Stack *st = new Stack();

st-> Push(new Listener());

st-> Push(new Listener());

Listener *tmp = st->Pop ();

tmp = st->op ();



delete st;

Описание методов вне шаблона

template <описание_параметров_шаблона>

возвр_тип имя_класса <параметры_шаблона>:: имя_функции (список_параметров функции)


Например:


template

T Stack::Pop()

{



}


Макрообработка


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



Классификация макропроцессоров