Конспект лекций по курсу «Объектно-ориентированное программирование»




НазваниеКонспект лекций по курсу «Объектно-ориентированное программирование»
страница3/4
Дата публикации11.08.2013
Размер0.59 Mb.
ТипКонспект
lit-yaz.ru > Информатика > Конспект
1   2   3   4
^

Дружественные функции



Ключевое слово friend (друг) служит спецификатором, уточняющим свойства функции. Оно дает функции-не-члену доступ к скрытым членам класса и предоставляет способ для обхода ограничений сокрытия данных в C++. Однако должна быть веская причина для обхода этих ограничений, поскольку они важны для надежного программирования.

Одна из таких причин состоит в том, что некоторые функции нуждаются в привилегированном доступе более чем к одному классу. Например, нам нужно определить оператор, который умножает матрицу Matrix на вектор Vector. Представление Matrix и Vector скрыто (private:)
class Matrix;
class Vector{

float v[4];

//...

friend Vector operator*(const Matrix&,const Vector&);

};
class Matrix{

Vector v[4];

//...

friend Vector operator*(const Matrix&,const Vector&);

};
Vector operator*(const Matrix& m,const Vector& v)

{

Vector r;

for(int i=0;i<4;i++){

r.v[i]=0;

for(int j=0;j<4;j++)r.v[i]+=m.v[i].v[j]*v.v[j];

}

return r;

}

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

Многие полезные типы являются вариантами других, и часто бывает утомительно создавать для каждого из них один и тот же код. Производный класс наследует описание базового класса; затем он может быть изменен добавлением новых членов, изменением существующих функций-членов и изменением прав доступа. Эта концепция полезна так же, как полезна таксономическая классификация: и слон и мышь являются млекопитающими. Информация о том, что млекопитающие – теплокровные высшие позвоночные, подробно изложена лишь однажды – в базовом понятии. В понятиях «слон» и «мышь» даются лишь характерные особенности этих животных.
^ Производный класс

Класс можно сделать производным от существующего с использованием следующей формы:

class имя_класса: (public|protected|private)opt имя_базового_класса

{

объявления членов

};

Ключевое слово class как всегда можно заменить словом struct, если подразумевается, что все члены открыты.

Ключевые слова public, protected и private используются для указания того, насколько члены базового класса будут доступны для производного. Ключевое слово protected – промежуточная форма доступа между public и private.

Рассмотрим пример:

<сначала – только члены данных>

<потом – функции-члены>

<потом – конструкторы>

//inheritance.h

class employee

{

public:

employee (char *name, double salary);

void print()const;

private:

char nm[30];

double slry;

};
enum language {ASSEMBLER,BASIC,PASCAL,C,CPP,SMALLTALK};
class programmer1

{

public:

programmer1(char *name, double salary, language lang);

void print()const;

private:

char nm[30];

double slry;

language lng;

};
class programmer2:public employee

{

public:

programmer2(char *name, double salary, language lang);

void print()const;

private:

language lng;

};

//inheritance.cpp

#include "inheritance.h"
employee::employee(char *name, double salary)

:slry(salary)

{

strcpy(nm,name);

}
void employee::print()const

{

cout<
}
programmer1::programmer1(char *name, double salary, language lang)

:slry(salary),lng(lang)

{

strcpy(nm,name);

}
void programmer1::print()const

{

cout<
switch(lng){

case C:cout<<"C";break;

case CPP:cout<<"C++";break;

default:cout<<"Another language";break;

}

}
programmer2::programmer2(char *name, double salary, language lang)

:employee(name,salary),lng(lang)

{}
void programmer2::print()const

{

employee::print();//печать информации о работнике

// должен использоваться оператор::, потому что

// print() была замещена в programmer2
// ошибка:

// cout<
// нельзя обратиться к закрытым членам базового класса
cout<<", Language:";//печать информации, относящейся

switch(lng){ // только к программисту

case C:cout<<"C";break;

case CPP:cout<<"C++";break;

default:cout<<"Another language";break;

}

}



Производный класс programmer2 имеет дополнительно 1 член данных lng и переопределяет функцию-член print(). Эта функция замещается, то есть производный класс включает реализацию функций-членов, отличную от базового класса. Это не имеет ничего общего с перегрузкой, когда смысл одного и того же имени функции может быть различным для разных сигнатур.
К закрытым членам базового класса обращаться нельзя. Там, где это приемлемо, можно воспользоваться защищенными (protected) членами вместо закрытых. Защищенный член ведет себя как открытый по отношению к члену производного класса и как закрытый по отношению к другим функциям.
Конструктор базового класса вызывается как часть списка инициализаторов в производном классе. Это вполне естественно и логично – для создания объекта нужно сначала создать ту его часть, которая относится к базовому классу.
employee e1("John",80),e2("Ben",90);

v.print();p.print();

programmer1 p1("Ron",200,BASIC);

p1.print();

programmer2 p2("Michael",500,C);

p2.print();

Как видите, использование классов programmer1 (без наследования) и programmer2 (с наследованием) здесь не отличается. Однако наследование имеет преимущество. Определение класса programmer2 как производного от employee делает его подтипом employee. Это значит, что объектом programmer2 можно пользоваться везде, где допустим employee. Например:

print_in_report(const employee& e){ e.print(); }

print_in_report(e1);

//print_in_report(p1); //error

print_in_report(p2); //OK
Копирование и срезка

employee arr_emp[3]={e1,p2,e2};

mas[0]=v;

// mas[0]=p1;//error

for(int i=0;i<3;++i)mas[i].print();
programmer2 p;

employee r=p;//срезка
Копируется только employee-часть programmer2. Этот эффект называют срезкой. Часто он является нежелательным. Одной из причин использования указателей и ссылок на объекты в иерархии является желание избежать срезки. Например:

employee *rptr=&r;//адрес работника

rptr=&p;//адрес программиста. Срезка отсутствует
Производные классы (Derived* можно использовать как Base*)

Функции-члены (замещение, обращение к ф-членам базового кл.)

Конструкторы и деструкторы (инициализация членов базового класса через конструктор базового класса)
UML

UML расшифровывается как Unified Modeling Language – унифицированный язык моделирования. Он является визуальным языком и предназначен для создания моделей программ. Под моделями программ понимается графическое представление программ в виде различных диаграмм, отражающих связи между объектами в программном коде.

Виды диаграмм:

  1. Диаграммы прецедентов

  2. Диаграммы видов деятельности

  3. Диаграммы взаимодействий (подвид – диаграмма последовательностей)

  4. Диаграммы классов

  5. Диаграммы состояний

  6. Диаграммы развертывания


Существует несколько нотаций и систем графического обозначения, предназначенных для той же цели, что и UML. Однако язык UML сейчас является стандартом де-факто.

Для чего используется язык UML

Прежде всего UML – язык общения с самим собой, членами команды и клиентами.

Если возникает необходимость обсудить проект и объяснить другим людям структуру классов, механизм взаимодействия объектов, причем без использования языка, подобного UML, то разговор получится путанным и бесконечным. Язык UML предлагает не только оптимальный путь написания проектов, созданных с применением объектных технологий, но также вынуждает разработчика более четко формулировать используемые им принципы (поскольку их нужно изложить в письменном виде).
Диаграмма классов

Описывает классы и отражает отношения, существующие между ними.

Класс обозначается прямоугольником, содержащим до трех элементов:

  • Имя класса

  • Имена методов (функций) класса

  • Имена данных-членов класса



Модификаторы доступа обозначаются знаками + public, # protected, - private.
Возможны следующие типы отношений между классами.

  • Отношение “is-a” – в этом случае один класс является подвидом другого класса

  • Отношение “has-a” – когда один объект одного класса «содержит» объект другого класса в качестве элемента данных.

Кардинальность (или кратность) элементов отношения показывают количество включаемых объектов.


^ Иерархия классов

Базовый класс (employee) может иметь несколько производных классов (teacher, manager, …):

class teacher: public employee{…};

class manager: public employee{…};

Производный класс (teacher), в свою очередь, сам может быть базовым классом (для english_teacher):

class english_teacher: public teacher{…};

Такой набор связанных классов называется иерархией классов. Иерархия имеет вид дерева (если не использовать множественное наследование).

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

  • Код используется повторно. Тип programmer2 использует существующий, хорошо проверенный код из employee;

  • Иерархия отражает взаимоотношения, свойственные проблемной области;

  • Различные полиморфные механизмы позволяют клиентскому коду рассматривать programmer2 в качестве подтипа employee, что упростит клиентский код.

^ Преобразование типов
Указатель на производный класс может быть преобразован в указатель на базовый класс.

У нас есть объект programmer2:
employee r("Vasya",80),*pr;

pr=&r;

pr->print();//employee::print

programmer2 p("Alex",300,CPP), *pp;

pr=pp=&p;//преобразование

pp->print();//programmer2::print

pr->print();//employee::print
Тот факт, что указатель теперь указывает на объект p типа programmer2, в данном случае в расчет не принимается.
Поля типа

Для того, чтобы через указатель на базовый класс вызывать подходящие функции (programmer2::print или employee::print), мы должны решить, какому типу на самом деле принадлежит объект, на который указывает указатель?

Для этого мы можем ввести в базовый класс поле типа, чтобы заинтересованные функции могли его проверить:
class employee{

public:

enum Rab_type{R,P};

Rab_type type;

employee():type(R){}

void print()const;

};

class programmer:public employee{

public:

programmer(){type=P;}

void print()const;

};
void employee::print()const{

cout<<"employee::print"<
}

void programmer::print()const{

cout<<"programmer::print"<
}
void print(employee* pr){

switch(pr->type){

case employee::R:

pr->print();

break;

case employee::P:

const programmer* p=static_cast(pr);

p->print();

break;

}

}

Добавление нового производного от employee класса подразумевает внесение изменений во все ключевые функции системы:

  1. в базовом классе employee в перечислимый тип добавляется новая константа;

  2. пишется производный класс (аналогично programmer);

  3. в функцию ::print добавляется новая ветвь.


Виртуальные функции
Виртуальные функции решают проблему, связанную с полем типа. Для того, чтобы сделать функцию виртуальной, нужно поставить ключевое слово virtual слева от объявления функции в классе. Например:
class employee_v{

public:

virtual void print()const;

};
При этом определение виртуальной функции синтаксически такое же, как и у обычной невиртуальной функции-члена:

void employee_v::print()const{

cout<<"employee_v::print"<
}
Виртуальная функция замещается в производном классе:

class programmer_v:public employee_v{

public:

virtual void print()const;

};
void programmer_v::print()const{

cout<<"programmer_v::print"<
}

Вызов виртуальной функции синтаксически такой же, как и обычной функции:
employee_v r1;

programmer_v p1;

r1.print();//employee_v::print

p1.print();//programmer_v::print

В этих двух случаях вызовутся функции print соответствующих классов. Так было бы и в случае невиртуальных функций print (т.е. без ключевого слова virtual).

void print_v(employee_v* pr){

pr->print();//employee_v::print или programmer_v::print

}

В этом случае мы вызываем функцию-член (print) по указателю pr на employee_v. В случае, если бы функция-член print была объявлена невиртуальной, то однозначно вызвалась бы функция employee_v::print (что определяется типом указателя). Однако, так как функция print виртуальна, вызывается либо employee_v::print, либо programmer_v::print, в зависимости от того, указывает pr на объект типа employee_v либо на объект типа programmer_v. Например:

employee_v r1,r2;

programmer_v p1,p2;

print_v(&r1);// employee_v::print

print_v(&p1);// programmer_v::print

Теперь мы можем запомнить указатели на различные объекты в массиве и вызвать print_v в цикле:

employee_v *record[4]={&r1,&p1,&r2,&p2};

for(int i=0;i<4;++i){

print_v(record[i]);

}

Вызовутся функции в таком порядке: employee_v::print, programmer_v::print, employee_v::print, programmer_v::print.

Так как наша функция print_v выродилась в единственный вызов функции-члена print, мы можем написать непосредственно (что эквивалентно предыдущему примеру):

for(int i=0;i<4;++i){

record[i]->print();

}

В случае, если мы используем виртуальные функции, добавление нового производного от employee класса не требует изменений ни в базовом классе, ни в коде, использующем виртуальные функции (в нашем случае этот код – приведенный выше цикл). Заметьте, что это будет работать, даже если указанный цикл был написан и откомпилирован до того, как производный класс programmer_v::print был вообще задуман! Данный факт служит краеугольным камнем объектно-ориентированных проектов и придает стабильность развивающейся программе.

Полиморфизм

Когда функции базового класса (employee_v) ведут себя «правильно» независимо от того, какой конкретно производный класс используется, это называется полиморфизмом. Тип, имеющий виртуальные функции, называется полиморфным типом. Для достижения полиморфного поведения в C++ вызываемые функции-члены должны быть виртуальными, и доступ к объекту должен осуществляться через ссылки или указатели. При непосредственных манипуляциях с объектом (без помощи указателя или ссылки) его точный тип известен компилятору, и поэтому полиморфизм времени выполнения не требуется.

^ Абстрактные классы
Многие классы схожи с классом employee_v в том, что они полезны как сами по себе (employee_v хранит информацию об имени и ставке работника и может распечатать эти данные), так и в качестве базы для производных классов (programmer_v и других). Для таких классов методы, описанные в предыдущем разделе (виртуальные функции), являются вполне достаточными. Однако не все классы соответствуют такому образцу. Некоторые классы, такие как figure, представляют собой абстрактную концепцию, для которой не могут существовать объекты. Класс figure имеет смысл только в качестве базы для производных классов.
class figure{

public:

void move_to(int x, int y){_x=x; _y=y;}

virtual void draw(HDC);

virtual void rotate(double);//angle measured in degrees

protected:

figure(int x,int y):_x(x),_y(y){}

int _x,_y;

};
Производные от figure классы переопределяют виртуальные функции и добавляют специфические члены данных.
class circle:public figure{

public:

circle(int x,int y,int r)

:figure(x,y),_r(r){}

virtual void draw(HDC);

virtual void rotate(double){};

private:

int _r;

};
class line:public figure{

public:

line(int x0, int y0,

int x1, int y1)

:figure(x0,y0),_dx(x1-x0),_dy(y1-y0)

{}

virtual void draw(HDC);

void rotate(double);

private:

int _dx,_dy;

};
Для figure невозможно разумно определить виртуальные функции:
void figure::draw(HDC){

assert(false);//этот код не должен быть вызван

}

void figure::rotate(double){

assert(false);//этот код не должен быть вызван

}
Попытка создания фигуры допустима, но неразумна:

figure f1(200,200);//фигура с центром, но без формы

circle c1(100,100,10);

line l1(120,100,130,105);

figure* mas[2]={&c1,&l1};

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

mas[i]->draw(hdc);
mas[i]->move_to(100,110);

mas[i]->draw(hdc);
mas[i]->move_to(100,140);

mas[i]->rotate(40);

mas[i]->draw(hdc); }
Включение
Виды включения: композиция и агрегация.

Шаблоны
В C++ ключевое слово template используется для обеспечения параметрического полиморфизма. Он позволяет применять один и тот же исходный код к разным типам, причем тип является параметром тела кода.

Допустим у нас есть класс stack3, который реализует концепцию стека символов (char).
class stack3

{

public:

stack3();

void push(char c);

char pop();

bool is_empty()const;

bool is_full()const;

private:

enum{ max_len=100};

int top;

char s[max_len];
};
Функции реализованы следующим образом:
stack3::stack3():top(0){}

void stack3::push(char c){

assert(top
s[top++]=c;

}

char stack3::pop(){

assert(top>0);

return s[--top];

}

bool stack3::is_empty()const {return top==0;}

bool stack3::is_full()const {return top==max_len;}
Мы можем использовать объекты этого класса следующим образом:
stack3 reverse_order(stack3 s){

stack3 tmp;

while(!s.is_empty()){

tmp.push(s.pop());

}

return tmp;

}

Пример шаблона класса:

template class stack4

{

public:

stack4(); //конструктор

void push(T c);

T pop();

bool is_empty()const;

bool is_full()const;

private:

enum{ max_len=100};

int top;

T s[max_len];

};
templatestack4::stack4():top(0){}
templatevoid stack4::push(T c){

assert(top
s[top++]=c;

}
templateT stack4::pop(){

assert(top>0);

return s[--top];

}
templatebool stack4::is_empty()const {return top==0;}
templatebool stack4::is_full()const

{return top==max_len;}
Префикс template указывает, что объявлен шаблон (template), и что в объявлении на месте T будет указан фактический тип. T – имя типа, а не обязательно класса.

Имя шаблона класса, за которым следует тип, помещенный в угловые скобки <>, является именем класса (определяемого шаблона) и его можно использовать точно так же, как имена других классов:

stack4 sc;

stack4 si;

stack4 sd;

Без использования шаблонов для реализации этого примера пришлось бы писать три определения классов, а с использованием шаблона – только одно определение шаблона.
Функция, работающая со стеком stack4:
stack4 reverse_order(stack4 s){

stack4 tmp;

while(!s.is_empty()){

tmp.push(s.pop());

}

return tmp;

}
Шаблон функции, работающий с любым стеком:
template STACK reverse_order(STACK s){

STACK tmp;

while(!s.is_empty()){

tmp.push(s.pop());

}

return tmp;

}

^ Исключение ненужных инстанцирований функций-членов
Следует заметить, что для каждого случая инстанцирования шаблона будет генерироваться свой набор функций-членов класса, например,
stack4 sc;//генерируется 5 функций

stack4 si1, si2;// генерируется 5 функций

//(количество создаваемых объектов не играет роли)

stack4 sd;//не генерируется новых функций, так как инстанцирование stack4 уже было выше
В нашем классе stack функции push и pop зависят от параметра шаблона, поэтому их генерирование необходимо. Так, функция push, сгенерированная для параметра char, работает не так, как функция push, сгенерированная для параметра int. В то же время функции is_empty и is_full от параметра шаблона не зависят. Функция is_empty для char абсолютно идентична функции is_empty для int. Для того, чтобы избежать генерирования лишних (полностью идентичных) функций, применяют следующий прием. Шаблонный класс создают производным от класса, не являющегося шаблоном. При этом в базовый класс помещают функции, не зависящие от параметра шаблона.

Применим данный прием для нашего стека:
class stack5_base

{

public:

stack5_base();

bool is_empty()const;

bool is_full()const;

protected:

enum{ max_len=100};

int top;

};
stack5_base::stack5_base()

:top(0)

{}
bool stack5_base::is_empty()const {return top==0;}
bool stack5_base::is_full()const {return top==max_len;}

template class stack5:public stack5_base

{

public:

void push(T c);

T pop();

private:

T s[max_len];

};
templatevoid stack5::push(T c){

assert(top
s[top++]=c;

}
templateT stack5::pop(){

assert(top>0);

return s[--top];

}

Для нашего случая количество сгенерированных функций уменьшается до минимально необходимого:
//к этому моменту сгенерировано 3 функции для stack5_base

stack5 sc;//генерируются 2 функции

stack5 si1, si2;// генерируются 2 функции

//(количество создаваемых объектов не играет роли)

stack5 sd;//не генерируется новых функций, так как инстанцирование stack4 уже было выше
^ Шаблоны с параметрами-значениями
В шаблонах stack3 и stack4 для хранения элементов используется массив на 100 элементов. В некоторых случаях указанное значение избыточно, и память расходуется нерационально. В некоторых случаях этого количества недостаточно и тогда шаблоны stack3 и stack4 не подходят для решения задачи. Как же спроектировать шаблон так, чтобы при необходимости получить заданное количество элементов не нужно было писать новый шаблон? Это можно сделать (по крайней мере) двумя путями.

Первый (уже известный вам) – передавать количество необходимых элементов в конструктор и в конструкторе динамически выделять память (а в деструкторе освобождать). При этом затрачивается время на выделение/освобождение памяти.

Второй путь – определить шаблон с параметром-значением, указывающим количество элементов. Перепишем шаблон stack4 с учетом этого. При этом для исключения ненужных инстанцирований функций-членов используем технику наследования, приведенную выше:
class stack6_base

{

public:

stack6_base();

bool is_empty()const;

protected:

int top;

};
stack6_base::stack6_base()

:top(0)

{}
bool stack6_base::is_empty()const {return top==0;}
template class stack6_n:public stack6_base

{

public:

bool is_full()const;

};
template bool stack6_n::is_full()const {return top==N;}

template class stack6:public stack6_n

{

public:

void push(T c);

T pop();

private:

T s[N];

};
template void stack6::push(T c){

assert(top
s[top++]=c;

}
template T stack6::pop(){

assert(top>0);

return s[--top];

}
Инстанцирование шаблона выглядит следующим образом:
stack6 sc;//стек char на 100 элементов

stack6 si1, si2;//стек int на 10 элементов

stack6 sd;//стек char на 5 элементов
Следует заметить, что шаблон функции reverse_order, приведенный выше, будет работать также и с классами, сгенерированными по шаблону stack6:
reverse_order(sс);
^ Стандартная библиотека
Ни одна программа приличных размеров не пишется с использованием только «голых» конструкций языка. Обычно используются различные готовые библиотеки, в которых другие программисты уже написали код, облегчающий работу в данной предметной области. Существуют различные библиотеки для различных целей. Например, библиотека MFC используется для облегчения программирования для Win32 по сравнению с «чистым» программированием под Win32 API. Win32 API предполагает процедурный интерфейс к Windows, а MFC – объектно-ориентированный. Компания Borland предоставляет для тех же целей библиотеку OWL.

C Run-Time Library – предоставляет функции и макросы для многих аспектов программирования. Эта бибилиотека разрабатывалась достаточно давно, поэтому она ориентирована на использование в программах на языке C (как видно из ее названия). Так как C++ совместим с C, то C Run-Time Library можно использовать и в C++-программах. Функции этой бибилиотеки (в том виде, в котором она существует в Microsoft Visual С++) разбиты на 19 категорий, среди которых:

поддержка функций с переменной длиной списка аргументов;

выделение памяти (malloc, calloc и др.);

контроль жизненным циклом процесса (exec, exit);

операции с памятью (memcpy, …);

классификация символов (isalpha);

преобразование данных (atoi, _itoa, tolower);

отладка (_ASSERT, _CrtCheckMemory);

операции с каталогами (_chdir);

операции с файлами (_filelength);

математические функции и поддержка операций с плавающей точкой (sin, cos, abs, floor, ldexp(mant,exponent));

ввод-вывод

потоковый ввод-вывод (getc, fwrite)

низкоуровневый ввод-вывод (_open, _write)

ввод-вывод с/на консоль и из/в порт (_cprintf, _inp)

время и дата в стиле C (clock, difftime)

алгоритмы поиска и сортировки (bsearch, qsort)

обработка ошибок в стиле С

функции над строками в стиле C (strcmp, strcpy) ,

1   2   3   4

Похожие:

Конспект лекций по курсу «Объектно-ориентированное программирование» iconОбъектно-ориентированное программирование
Курсовая работа по специальности 230201 «Информационные системы и технологии»: М. 2012 г., Мирэа, факультет Информационных технологий,...

Конспект лекций по курсу «Объектно-ориентированное программирование» iconКонспект лекций «Логистика. Конспект лекций»
Конспект лекций соответствует требованиям Государственного образовательного стандарта высшего профессионального образования

Конспект лекций по курсу «Объектно-ориентированное программирование» iconГрегори К. Использование Visual C++ Специальное издание. М.; Спб
Буч Г. Объектно-ориентированное проектирование с примерами применения. – М.: Конкорд, 1992. – 519 с

Конспект лекций по курсу «Объектно-ориентированное программирование» iconКонспект лекций доцента и. А. Волковой по курсу «системы программирования»
Система программирования – комплекс программных инструментов и библиотек, который поддерживает создание и существование программного...

Конспект лекций по курсу «Объектно-ориентированное программирование» iconКонспект лекций по дисциплине вгипу, 2009 Конспект лекций по дисциплине «Управление персоналом»
Крупица В. В., Яшкова Е. В., Егоров Е. Е. Управление персоналом: Конспект лекций по дисциплине – вгипу, 2009

Конспект лекций по курсу «Объектно-ориентированное программирование» iconКонспект лекций. (Электронный учебник) Минск: бгэу, 2010. Тема 1...
Короленок Г. А. Менеджмент в торговле. Конспект лекций. (Электронный учебник) Минск: бгэу, 2010

Конспект лекций по курсу «Объектно-ориентированное программирование» iconПоляков Д. Б., Круглов И. Ю. Программирование в среде турбо паскаль (версия 5)
Зуев Е. А. Программирование на языке turbo pascal 0, М.: Радио и связь, 1993. 384

Конспект лекций по курсу «Объектно-ориентированное программирование» iconУчебное пособие к курсу лекций «Введение в современную литературу»
Предлагаемое издание является учебным пособием к вузовскому курсу «Введение в современную литературу», который читается для студентов...

Конспект лекций по курсу «Объектно-ориентированное программирование» iconКонспект лекций, которые проф. Пугинский Б. И. читал в весеннем семестре...
Закончились а для кого-то они даже и не начинались, жалко. Закончились объединив как никогда прежде два потока. Закончились превратившись...

Конспект лекций по курсу «Объектно-ориентированное программирование» iconЛитература к курсу лекций
Алексеев П. В. Философы в России 19-20 столетий. Биографии, идеи, труды. 3-е изд. М., 1999



Образовательный материал



При копировании материала укажите ссылку © 2013
контакты
lit-yaz.ru
главная страница