Слайд 1Здравствуйте!
Меня зовут Клышинский Эдуард Станиславович
eklyshinsky@hse.ru
Слайд 2Программирование
5, 6 модули
(Объектно-ориентированное программирование)
МИЭМ НИУ ВШЭ, Клышинский Э.С.
2013 г.
Слайд 3Рекомендуемая литература
М. Шлее Qt 4.8. Профессиональное программирование на C++ (655 руб.)
Б.
Страуструп Программирование. Принципы и практика использования C++ (2560 руб.)
С. Прата Язык программирования C++ (C++11). Лекции и упражнения (1909 руб.)
http://www.cplusplus.com/
http://habrahabr.ru/
Слайд 5Жизненная философия
Шу (семинары и лабораторные) – ученик не способен задать правильные
вопросы и понять ответы
Ха (лекции) – ученик постоянно совершенствует свою технику и спрашивает «Почему?»
Ри (курсовая) – ученик постоянно экспериментирует, смешивает техники, пробует разработать собственный стиль.
Слайд 6Жизненная философия
У нас нет времени делать это последовательно!
Слайд 7Как быть хорошим программистом
The first requirement for programming is a passion
for the work, a deep need to probe the mysterious space between human thoughts and what a machine can understand; between human desires and how machines might satisfy them.
“How to be a Woman Programmer,” Ellen Ullman
Слайд 8Как быть хорошим программистом
The second requirement is a high tolerance for
failure. Programming is the art of algorithm design and the craft of debugging errant code.
“How to be a Woman Programmer,” Ellen Ullman
Слайд 9Как быть хорошим программистом
You need the willingness to fail all the
time. You have to generate many ideas and then you have to work very hard only to discover that they don’t work. And you keep doing that over and over until you find one that does work.
John Backus,
inventor of the Fortran programming language
Слайд 10Что нам предстоит
Лекции
Семинары
Лабораторные работы (5 модуль):
Разработка контейнера.
Визуальный интерфейс для разработанного
контейнера, сохранение.
Контейнер для базового класса.
Разработка большого приложения.
Слайд 11Система оценок
Лабораторные работы (5 модуль):
1 – 1 балл,
2 – 1 балл,
3
– 2 балла,
4 – 2 балла.
Зачет – 4 балла.
Старосты! Нам нужны актуальные списки!
Слайд 12Система оценок
Указанные баллы являются максимумом, который можно получить за работу.
Наличие обоснованных
претензий у преподавателя ведет к снижению баллов.
Слайд 13Система оценок
К хорошим программистом приходит Дед Мороз.
К плохим – DeadLine.
Слайд 14Система оценок
По лабораторным работам назначается срок сдачи. Если работа сдается на
неделю позже по вине студента, максимальный балл уменьшается на 25%, если на две – на 50%, если позже – оценка не ставится.
Слайд 15Система оценок
Лабораторные работы, выполненные в противоречии правилам хорошего тона
НЕ ПРИНИМАЮТСЯ!!!
Слайд 16Система оценок
Если сумма баллов за ниже 4 баллов, берется целая часть
от полученной суммы. Если сумма баллов выше 4, то проводится арифметическое округление.
Слайд 17Курсовая работа (л/р №4)
Команда из n∈[1;2] человек должны написать n*[400;600] строк
кода по выбранной теме.
Требования на «отлично»:
классы
наследование
перегрузка операторов
шаблоны
полиморфизм
Слайд 18Курсовая работа
Команда из n∈[1;2] человек должны написать n*[400;600] строк кода по
выбранной теме.
Требования на «хорошо»:
классы
наследование
Слайд 19Курсовая работа
Команда из n∈[1;2] человек должны написать n*[400;600] строк кода по
выбранной теме.
Требования на «удовлетворительно»:
классы
Слайд 21Иерархия языков
Процедурное программирование
Ассемблер
Структурное программирование
Объектно-ориентированное программирование
Что понимает компьютер.
Как человеку проще записывать действия.
Как
человеку проще описывать данные.
Как человеку проще мыслить.
Слайд 22Понятие объекта и класса
Весь мир состоит из объектов, поэтому человеку проще
манипулировать именно объектами, а не наборами переменных.
Слайд 23Понятие объекта и класса
Что объединяет эти вещи?
Слайд 24Понятие объекта и класса
Стул — мебельное изделие для сидения одного человека, со
спинкой, с подлокотниками или без них, с высотой сиденья, функционально удобной при соотношении его с высотой стола.
Основные части стула — сиденье и спинка, в типичном стуле сиденье опирается на четыре ножки, иногда в конструкцию стула входят подлокотники. Спинка может иметь незаполненный промежуток до сиденья или выполняться на всю высоту, без промежутка. Также промежуток между спинкой и сиденьем может быть заполнен декоративным элементом.
Википедия
Слайд 25Понятие объекта и класса
Стул — мебельное изделие для сидения одного человека, со
спинкой, с подлокотниками или без них, с высотой сиденья, функционально удобной при соотношении его с высотой стола.
Основные части стула — сиденье и спинка, в типичном стуле сиденье опирается на четыре ножки, иногда в конструкцию стула входят подлокотники. Спинка может иметь незаполненный промежуток до сиденья или выполняться на всю высоту, без промежутка. Также промежуток между спинкой и сиденьем может быть заполнен декоративным элементом.
Википедия
Это свойства объекта
Слайд 26Понятие объекта и класса
Стул — мебельное изделие для сидения одного человека, со
спинкой, с подлокотниками или без них, с высотой сиденья, функционально удобной при соотношении его с высотой стола.
Основные части стула — сиденье и спинка, в типичном стуле сиденье опирается на четыре ножки, иногда в конструкцию стула входят подлокотники. Спинка может иметь незаполненный промежуток до сиденья или выполняться на всю высоту, без промежутка. Также промежуток между спинкой и сиденьем может быть заполнен декоративным элементом.
Википедия
Это функциональность объекта
Слайд 27Понятие объекта и класса
Класс = данные + методы работы с ними
Слайд 28Понятие объекта и класса
Эйдос Платона - то подлинное, что дается в
умопостижении, в отвлечении от нашего мнения о вещи и от чувственных впечатлений, которые отражают только материальное бытие вещи. В отличие от идеи эйдос уже не генерализует, но наоборот выделяет и отличает вещь от других вещей.
Слайд 29Понятие объекта и класса
Класс – некоторый шаблон, описывающий создаваемые объекты (эйдос,
идея).
Объекты – фрагменты оперативной памяти, расположение элементов которых описывается заданным классом (элементы окружающего мира).
Слайд 30Понятие объекта и класса
Класс (шаблон)
class Ui_MainWindow
{
public:
QWidget *centralWidget;
QLineEdit *Pix_path;
QPushButton
*pushButton_3;
QPushButton *pushButton_2;
QLineEdit *lineEdit;
QPushButton *pushButton_9;
QPushButton *pushButton;
QProgressBar *progressBar;
void setupUi(QMainWindow *MainWnd);
void retranslateUi(QMainWindow *MainWnd);
};
объекты (данные)
методы
Слайд 31Абстракция
Буч Г. Объектно-ориентированный анализ и проектирование
Слайд 32Классом может быть и абстрактная сущность
- ...начинается на _M_, - продолжала
она. - Они рисовали мышеловки, месяц, математику, множество... Ты когда-нибудь видела, как рисуют множество?
- Множество чего? - спросила Алиса.
- Ничего, - отвечала Соня. - Просто множество!
- Не знаю, - начала Алиса, - может...
- А не знаешь - молчи, - оборвал ее Болванщик.
Слайд 33
Классом может быть и абстрактная сущность
- ...начинается на _M_, - продолжала
она. - Они рисовали мышеловки, месяц, математику, множество... Ты когда-нибудь видела, как рисуют множество?
- Множество чего? - спросила Алиса.
- Ничего, - отвечала Соня. - Просто множество!
- Не знаю, - начала Алиса, - может...
- А не знаешь - молчи, - оборвал ее Болванщик.
Кстати, это не наши методы. Лучше задавайте вопросы.
Слайд 34Классом может быть и абстрактная сущность
Множество
Свойства: массив элементов, количество элементов в
массиве
Методы: добавить элемент, удалить элемент, очистить, выбрать элемент, найти элемент
Слайд 35Классом может быть и абстрактная сущность
Заслуга
Свойства: дата, место, наименование
Методы: отобразить, установить
дату (в целях проверки корректности входных данных), вернуть дату
Слайд 36Достоинства: модульность
Буч Г. Объектно-ориентированный анализ и проектирование
Слайд 37Достоинства: простота
Буч Г. Объектно-ориентированный анализ и проектирование
Слайд 38Инкапсуляция
Механизм языка программирования, ограничивающий доступ к составляющим объект компонентам (методам и
свойствам), делает их приватными, то есть доступными только внутри объекта. Важно понимать, что к инкапсулированной переменной можно обратиться при написании реализации класса, но при его использовании обращение к ней невозможно.
Слайд 39Инкапсуляция
Следует различать интерфейс класса, то есть то, что он предоставляет для
использования другим фрагментам кода, от реализации класса, то есть описания собственно механизма функционирования.
Слайд 40Синтаксис
class TheClassName // class имя_класса
{
public: // Магической слово
int property1;
// свойства класса
double property2, property3;
int methodFoo(int a, char *b); // методы
float methodBar();
};
Слайд 41Синтаксис
class TheClassName // порядок не важен
{
public: // Магической слово
double
property2, property3;
int methodFoo(int a, char *b); // методы
int property1; // свойства класса
float methodBar();
};
Слайд 42Синтаксис
class TheClassName // порядок не важен
{public:double property2,property3;int methodFoo(int a,char*b);int property1;float methodBar();};
// Да, как и форматирование кода
Слайд 43Синтаксис
int TheClassName::methodFoo(int a, char *b)
{
property3=a; property1=strlen(b);
return
0;
}
float TheClassName::methodBar()
{
property2=0; return 0.;
}
Слайд 44Синтаксис
#include “myclass.h”
int main()
{TheClassName obj1, *obj2=&obj1;
obj1.property1=0;
obj1.methodBar();
obj2->methodFoo(10, “строка”);
}
Слайд 45Синтаксис
class TheClassName2 //
{
public:
TheClassName *objects;
int count;
void
add(TheClassName &obj);
void delete(int pos);
};
Слайд 46Синтаксис
class TheClassName2 //
{
public:
TheClassName *objects;
int count;
void
add(TheClassName &obj);
void delete(int pos);
};
Ссылка на параметр, параметр может изменяться внутри функции.
Слайд 47Область видимости
public – то, что написано в этой строке будет доступно
«снаружи» класса.
public: – то, что написано ниже будет доступно «снаружи» класса.
private – то, что написано в этой строке будет доступно только методам класса.
private: – то, что написано ниже будет доступно только методам класса.
Слайд 48Область видимости
По умолчанию свойства и методы класса являются private, поэтому забыв
написать public мы лишаем класс интерфейса.
Структура – тоже класс и она может иметь методы. В целях совместимости все ее члены и методы по умолчанию public.
Слайд 49Область видимости
Общее правило.
По умолчанию всё, что не обязано быть доступно снаружи,
обязано быть private.
Пожалейте других разработчиков!
Слайд 50Область видимости
Общее правило.
Правила хорошего тона предписывают описать сперва открытые члены класса,
а потом закрытые.
Пожалейте других разработчиков!
Слайд 51Пример
Мы пишем экономическую стратегию (игра). Необходимо создать класс, описывающий источник ресурсов.
Время
дискретно, внешний класс (игра) сообщает о его течении.
Слайд 52Пример
Свойства: название ресурса, количество ресурса, производительность, интервал производства, количество произведенных ресурсов,
число работников, максимальное число работников.
Методы: прошло время
Слайд 53Пример
class Mine // Часть свойств опущено
{
public:
char *resName; // Имя
ресурса
// Сколько есть ресурса, сколько произведено
int overall, produced;
// Число работников, мах число работников
int workerCount, maxWorkerCount;
// Тикает время
bool tick();
};
Слайд 54Пример
bool Mine::tick()
{double tmp;
curTime++;
if( curTime>=interval )
{curTime
= 0;
tmp = ((double) workerCount) /
maxWorkerCount;
produced += productivity * tmp;
return true;
}
return false;
}
Слайд 55Пример использования
int main()
{Mine mn;
mn.maxWorkerCount=0;
mn.productivity=-1
mn.resName=(0xFACE8D00);
mn.tick();
}
И кого будут ругать?
Слайд 56Первый принцип программиста
КРУГОМ ВРАГИ!
Пишите код так, как будто сопровождать его будет
склонный к насилию психопат, который знает, где вы живете.
(с) Стив Макконнелл
Слайд 57Второй принцип программиста
Надо инициализировать переменные
Буратино дали пять яблок, сколько яблок у
него стало?
Неизвестно, так как неизвестно, сколько яблок у него было до того.
Слайд 58Третий принцип программиста
Работа выполнена, если за нее заплачены деньги.
(Раз уж мы
об этом заговорили.)
Слайд 59Четвертый принцип программиста
КОПИРОВАНИЕ – ЗЛО!
(Раз уж мы об этом заговорили.)
Слайд 60Как быть?
Принцип 1 вступает в противоречие с принципом 2. Мы не
можем спрятать переменные, так как их надо инициализировать. И мы не можем оставить их доступными, так как возможны ошибки.
Слайд 61Как быть?
Надо завести функцию инициализации и спрятать все данные!
Но наши коллеги
забудут ее вызвать. ☹
Слайд 62Конструктор
В объектно-ориентированном программировании конструктор класса – специальный блок инструкций, вызываемый при
создании объекта.
Слайд 63Деструктор
В объектно-ориентированном программировании деструктор класса – специальный блок инструкций, вызываемый при
уничтожении объекта.
Слайд 64Пример
class Mine
{public:
Mine(); // Конструктор по умолчанию
Mine(char *name,
int amount, int prdctvt, int intrvl); // Инициализирующий конструктор
Mine(Mine &mn); // Копирующий конструктор
Mine(Mine &&mn); // Конструктор переноса
~Mine(); // Деструктор
bool tick();
bool addWorker();
bool getWorker();
Слайд 65Пример
private:
char *resName; // Имя ресурса
// Сколько есть ресурса, сколько
произведено
int overall, produced;
// Число работников, мах число работников
int workerCount, maxWorkerCount;
};
Слайд 66Пример
Mine::Mine()
{resName=NULL;
overall=0;
produced=0;
workerCount=0;
maxWorkerCount=0;
// И так далее
}
Слайд 67Пример
Mine::Mine(char *name, int amount, int prdctvt,
int intrvl)
{resName=new char [strlen(name)];
overall=amount;
produced=0;
workerCount=0;
maxWorkerCount=0;
// И так далее
}
Слайд 68Пример
bool Mine::addWorker()
{if( workerCount == maxWorkerCount )
return false;
workerCount++; return
true;
}
bool Mine::getWorker()
{if( workerCount == 0 ) return false;
workerCount--; return true;
}
Слайд 69Пример
int main()
{Mine mine1;
Mine mine2(“Mine2”, 100, 1, 50);
Mine mine3(mine2);
// Дальше
идет код
}
Слайд 70Выделение памяти
Функция malloc ничего не должна знать про конструктор и деструктор,
она только выделяет память. Поэтому используется оператор new.
Выделение переменной
указатель = new тип;
Выделение массива
Указатель = new тип [кол-во элементов];
Слайд 71Выделение памяти
Mine *newMine, *mine2, *mine3;
newMine=new Mine;
newMine->addWorker();
Слайд 72Выделение памяти
Для вызова инициализирующих конструкторов можно использовать конструкцию
указатель = new тип
( параметры );
Mine *newMine, *mine2, *mine3;
newMine=new Mine;
newMine->addWorker();
mine2=new Mine(newMine);
mine3=new Mine(“Mine3”, 900, 2, 30);
Слайд 73Выделение памяти
int **arr;
int cnt, i, j;
scanf(“%d”, cnt);
arr=new int * [cnt];
for( i=0;
i{arr[i]=new int[cnt];
for( j=0; j arr[i][j]=i+j;
}
Слайд 74Освобождение памяти
Функция free ничего не знает про деструктор, поэтому надо пользоваться
delete.
Удаление переменной
delete указатель;
Удаление массива
delete [] указатель;
Слайд 75Освобождение памяти
Внимание!
Всё, что выделялось с квадратными скобками с ними же и
удаляется!
delete null; или delete [] null;
Это работает!
Слайд 76Конструктор вызывается
… при любом создании объекта:
создании переменной (локальной или глобальной –
не важно);
динамическом выделении памяти (использование new);
передаче параметра-объекта в функцию;
создании временного объекта для вычисления результата;
…
Слайд 77Деструктор вызывается
… при любом уничтожении объекта:
закончилось время жизни (зона видимости) переменной
(локальной или глобальной – не важно);
освобождении динамически выделенной памяти (использование delete);
передаче параметра-объекта в функцию (после возвращения из функции);
удалении временного объекта для вычисления результата (после вычислений);
…
Слайд 78Для временных переменных
… для снятия копии вызывается копирующий конструктор.
Если его
нет, снимается побайтовая копия с копируемого объекта.
Слайд 79Пример
class Stack
{public:
Stack();
~Stack();
void push(int);
int
pop();
private:
int *data;
int count;
};
void func(Stack st)
{
// Много кода
}
void func2()
{Stack s;
// Код
func(s);
// Еще код
}
Слайд 80Пример
void func2()
{Stack s;
func(s);
void func(Stack s)
{
} // почти delete &s;
Слайд 81Чему учит нас Летающий Макаронный Монстр?
9. Лучше бы ты не передавал
объекты по значению, равно как и массивы. Это не способствует проявлениям любви к тебе со стороны ближних.
Слайд 82Значит передавать по ссылке?
-- Кристофер Робин, ты должен сбить шар из
ружья. Ружье у тебя с собой?
-- Понятно, с собой,-- сказал Кристофер Робин.-- Но если я выстрелю в шарик, он же испортится!
-- А если ты не выстрелишь, тогда испорчусь я,-- сказал Пух.
Слайд 83Да, но только по константной
void func(const Stack &st)
{
// Так можно
int n=st.getCount();
if( st.getCount==0 )
{
// Так нельзя
st.push(444);
}
}
class Stack
{public:
Stack();
~Stack();
void push(int);
int pop();
int getCount() const;
private:
int *data;
int count;
};
Слайд 84Да, но только по константной
int Stack::getCount() const
{
count++; // нельзя
return
count; // можно
}
class Stack
{public:
Stack();
~Stack();
void push(int);
int pop();
int getCount() const;
private:
int *data;
int count;
};
Слайд 85Переменная видна с момента ее объявления до соответствующей закрывающей скобки
Область видимости
переменной
Слайд 86int Stack::push(int d)
{int i, t;
int *tmp;
tmp=new int[count+1];
for( i=0;
i
{t=data[i]
tmp[i]=t;
}
tmp[count++]=d;
delete [] data;
data=tmp;
}
Переменная видна с момента ее объявления до соответствующей закрывающей скобки
Область видимости переменной
Слайд 87int Stack::push(int d)
{int i;
int *tmp;
tmp=new int[count+1];
for( i=0; i
i++ )
{int t=data[i]
tmp[i]=t;
}
tmp[count++]=d;
delete [] data;
data=tmp;
}
Переменная видна с момента ее объявления до соответствующей закрывающей скобки
Область видимости переменной
Слайд 88int Stack::push(int d)
{
int *tmp;
tmp=new int[count+1];
for( int i=0; i
i++ )
{int t=data[i]
tmp[i]=t;
}
tmp[count++]=d;
delete [] data;
data=tmp;
}
Переменная видна с момента ее объявления до соответствующей закрывающей скобки
Область видимости переменной
Слайд 89Область видимости переменной
int somefunc(int **data, int cnt)
{int i, sum;
for( sum=0,
i=0; i for( int i=0; i sum+=data[::i][i];
return sum;
}
Слайд 90Область видимости переменной
int somefunc(int **data, int cnt)
{int i, sum;
for( sum=0,
i=0; i
for( int i=0; i sum+=data[::i][i];
return sum;
}
i из цикла
i верхнего уровня
Слайд 91Лекция 3
Разработка визуальных интерфейсов в Qt
Слайд 92Указатель на функцию
int (*pt2Function)(float, char, char);
pt2Function представляет собой переменную, указывающую на
начало функции, которая возвращает int и принимает float, char и char.
Слайд 93Указатель на функцию
int f1(float a, char b, char c)
{ // place
your code here }
int f2(float a, char b, char c)
{ // place your code here }
int main()
{int (*pt2Function)(float, char, char);
pt2Function=f1;
pt2Function(1.1, ‘d’, ‘f’);
}
Слайд 94Зачем это надо?
int cmpMines1(const Mine &m1, const Mine &m2)
{if(m1.getProduct()==m2.getProduct())
return
0;
// …
}
int cmpMines2(const Mine &m1, const Mine &m2)
{if(m1.getInterval()>m2.getInterval())
return 1;
// …
}
Слайд 95Зачем это надо?
void sortMines(Mine *mines, int count, int (*cmpFunc) (const Mine
&m1, const Mine &m2) )
{
for( int i=0; i for( int j=i+1; j if(cmpFunc(mines[i],mines[j])<0)
swapMines(mines[i], mines[j]);
}
Слайд 96Зачем это надо?
int main()
{ Mines mns[100];
int mCount;
// Some initializations here
if( sortByProd )
sortMines( mns, mCount, cmpMines1);
else if( sortByInterval )
sortMines( mns, mCount, cmpMines2);
// and so on
}
Слайд 97Удобнее так
typedef int (*cmpFuncType) (const Mine &m1, const Mine &m2) );
Теперь
cmpFuncType – это тип указателя на функцию сравнения двух шахт.
Слайд 98Удобнее так
enum SortModes={smProd, smInterval, smWorkers};
class MinesCollection
{// Some code here
public:
void setSortingMode(SortModes mode);
void sortMines();
// code
private cmpFuncType ptr;
}
Слайд 99Удобнее так
enum SortModes={smProd, smIntr, smWrk};
void MinesCollection::setSortingMode(SortModes mode)
{ switch( mode )
{case
smProd: ptr=cmpMines1; return;
case smIntr: ptr=&cmpMines2; return;
// А еще добавить по возрастанию или убыванию.
}
}
Слайд 100Для классов
// Взято с http://www.newty.de/fpt/fpt.html
class TMyClass {
public:
int DoIt(float a, char b, char c);
int DoMore(float a, char b, char c) const;
// more code
};
Слайд 101Для классов
int (TMyClass::*pt2Member)(float, char, char) = null;
int (TMyClass::*pt2ConstMember)(float, char, char) const
= null;
pt2ConstMember = &TMyClass::DoMore;
pt2Member = &TMyClass::DoIt;
// pt2Member может показывать и на &DoMore
Слайд 102Для классов
TMyClass instance1;
int result3 = (instance1.*pt2Member)(12, 'a', 'b');
int result4
= (*this.*pt2Member)(12, 'a', 'b');
TMyClass* instance2 = new TMyClass;
int result4 = (instance2->*pt2Member)(12, 'a', 'b');
delete instance2;
Слайд 105Вы не видите ничего странного?
#include
class Counter : public QObject
{ Q_OBJECT
public:
Counter() { m_value = 0; }
int value() const { return m_value; }
public slots: void setValue(int value);
signals: void valueChanged(int newValue);
private: int m_value;
};
Слайд 106Вы не видите ничего странного?
#include
class Counter : public QObject
{ Q_OBJECT
public:
Counter() { m_value = 0; }
int value() const { return m_value; }
public slots: void setValue(int value);
signals: void valueChanged(int newValue);
private: int m_value;
};
Слайд 107Сигналы и слоты в Qt
void Counter::setValue(int value)
{
if (value !=
m_value) {
m_value = value;
emit valueChanged(value);
}
}
Слайд 108Сигналы и слоты в Qt
Counter a, b;
QObject::connect(&a, &Counter::valueChanged,
&b, &Counter::setValue);
a.setValue(12); // a.value() == 12, b.value() == 12
b.setValue(48); // a.value() == 12, b.value() == 48
Слайд 109Сигналы и слоты в Qt
Counter a, b, c, d;
QObject::connect(&a, &Counter::valueChanged,
&b, &Counter::setValue);
QObject::connect(&a, &Counter::valueChanged,
&c, &Counter::setValue);
QObject::connect(&b, &Counter::valueChanged,
&d, &Counter::setValue);
a.setValue(12); // a==12, b==12, c==12, d==12
b.setValue(48); // a==12, b==48, c==12, d==48
Слайд 110Сигналы и слоты в Qt
“+”
На один сигнал можно «повесить» много слотов.
Инициируя сигнал мы можем не беспокоиться о их количестве.
“-”
Отследить вызовы всех слотов – большая проблема.
Слайд 111Сигналы и слоты в Qt
class QButton
{ …
public slots: void clicked();
}
void
MainWindow::on_pushButton_13_clicked()
QObject::connect(button_13, clicked, myWindow,
on_pushButton_13_clicked)
Слайд 113Вы не видите ничего странного?
#include
class Counter : public QObject
{ Q_OBJECT
public:
Counter() { m_value = 0; }
int value() const { return m_value; }
public slots: void setValue(int value);
signals: void valueChanged(int newValue);
private: int m_value;
};
Слайд 114Вы не видите ничего странного?
#include
class Counter : public QObject
{ Q_OBJECT
public:
Counter() { m_value = 0; }
int value() const { return m_value; }
public slots: void setValue(int value);
signals: void valueChanged(int newValue);
private: int m_value;
};
Нет, наследование мы еще не проходили.
Слайд 115А это что?
#include
class Counter : public QObject
{ Q_OBJECT
public:
Counter()
{ m_value = 0; }
int value() const { return m_value; }
public slots: void setValue(int value);
signals: void valueChanged(int newValue);
private: int m_value;
};
Слайд 116А это что?
Это inline-функция.
Иногда вызов функции стоит дороже самой функции. Если
такую функцию оформить как inline, то вместо генерации кода вызова функции будет подставлено тело функции.
И программа станет чуть-чуть быстрее.
Слайд 117Но есть нюанс
Inline-функция не будет считаться компилятором таковой, если она содержит
в себе операторы перехода, то есть
if, switch, goto, for, while, do while
Слайд 118Model-View-Controller
Модель – хранит данные.
Отображения – отображают их.
Контроллеры – позволяют вводить данные.
Такая
модель позволяет разделять сущности и делать их независимыми.
Слайд 119Спроектируем игру
Железная дорога на квадратной сетке. Паровозы по рельсам возят грузы
между станциями, грузы появляются из шахт, фабрик, домов и потребляются ими же.
Слайд 120«Юный логистик»
Храним карту как двумерный массив структур. Структура показывает что есть
в этой ячейке: есть ли здание, какого вида, что производит, что потребляет, какие ресурсы тут лежат, есть ли железная дорога, в каких направлениях она идет, есть ли паровоз, рельеф квадрата. Карта умеет всё это хранить, заводить новые объекты, отвечает за производство.
Карта умеет себя отображать. На действия пользователя реагирует интерфейс, модифицируя карту.
Слайд 121«Юный логистик»
Храним карту как двумерный массив структур. Структура показывает что есть
в этой ячейке: есть ли здание, какого вида, что производит, что потребляет, какие ресурсы тут лежат, есть ли железная дорога, в каких направлениях она идет, есть ли паровоз, рельеф квадрата. Карта умеет всё это хранить, заводить новые объекты, отвечает за производство.
Карта умеет себя отображать. На действия пользователя реагирует интерфейс, модифицируя карту.
Добавление сетевого режима невозможно.
Слайд 122«Юный логистик 2.0»
Карта хранит информацию о рельефе и дороге, хранит список
всех зданий, знает привязку зданий к карте. Станция хранит ресурсы и имеет ссылки на здания в окрестности, умеет определять потребности и раздавать ресурсы. Остальные здания знают какие они, хранят ресурсы, знают что производят и свои потребности, умеют производить. Паровоз знает что на него загружено, где он находится, каков маршрут.
Интерфейс умеет отображать карту, здания, паровоз, используя информацию о них.
Вводит данные с элементов управления.
Слайд 123«Юный логистик 2.1 (несколько игроков)»
Надо объектам добавить информацию о их принадлежности.
Слайд 124«Юный логистик 2.2 (сеть)»
Надо на место контроллеров добавить объект, который получает
информацию из сети. На место отображения добавить объект, который отправляет информацию в сеть. Надо создать сервер, который будет помогать обмениваться информацией игрокам.
Слайд 125«Юный логистик 2.3 (сеть+ИИ)»
Надо добавить объект, который играет за ИИ. Информацию
он будет получать как отображение, оказывать влияние на поле при помощи контроллеров.
Слайд 126Результаты контрольной
Умеют проектировать – 24 человека (17 из С)
Не умеют проектировать
– 28 человек (12 из С)
Не умеют программировать – 11 человек (4 из С)
Списывала примерно половина.
Слайд 127Самые интересные ошибки
class Cobe
{privat
int *x1, *y1, *x2, *y2, z1[MAX_INT], z2[MAX_INT];
Cobe *list,
list2[6];
public
void add Cube (Cobe &c);
};
Слайд 128Самые интересные ошибки
person()
{int len=0;
int hight=0;
};
Слайд 129Что хотелось увидеть
class Cube
{ Cube();
Cube(const Point &_p1, const Point &_p2,
const Point &_p3);
Cube( const Cube &c);
void addCube( const Cube *c);
private:
Point p1, p2, p3;
Cube **neighbours;
int neigh_count;
};
Слайд 130Что хотелось увидеть
class Cube;
struct CubesList
{Cube *cub;
CubeList *next;
};
class Cube
{…
private:
float
x, y, z, size, angle1, angle2;
CubeList neighbours;
};
Слайд 131Что хотелось увидеть
class Cube
{…
private:
int id;
int *neighbours;
int neigh_size;
};
Слайд 132Результаты контрольной
Умеют проектировать – 14 человек
Не умеют проектировать – 25 человек
Не
умеют программировать – 6 человек
Списывала примерно половина.
Слайд 133Самые интересные ошибки
class ClassName
{privet:
int x1, y1, x2, y2, r1, r2;
public
void
interception( /* совсем ничего? */ )
{writeln (“ищем пересечение”); }
};
Слайд 134Что хотелось увидеть
class Circle
{public:
Circle();
Circle(float x, float y, float r);
Circle( const Circle &c);
bool intersection( const Circle &c);
private:
float center_x, center_y, radius;
};
Слайд 135Да, у них было проще
Садись в ногах, правды нет.
Слайд 137Некоторые классы умеют так
QString str1, str2;
std::ofstream fil(“file.txt”);
str1=“asdfgh”;
str2=“qwerty”;
str1+=str2;
str2=str1+”zxcvbn”;
fil
Слайд 139Ответ
Мы имеем возможность перегружать операторы
+ - * / %
+= -= *=
/= %=
+a -a
++a a++ --a a--
&& || !
& | ~ ^
&= |= ^=
<< >> <<= >>=
=
== !=
< > >= <=
&a *a a-> a->* a.*
() []
(type)
,
sizeof
new, new[]
delete, delete []
И еще некоторые
Слайд 140А как именно?
Функция называется operator @ ,
где @ - один из
перечисленных выше операторов
class MyClass
{ int a;
public:
MyClass();
MyClass(int d) { a=d; }
MyClass & operator += (const MyClass &obj);
int getA( return a; }
};
Слайд 141А как именно?
MyClass& MyClass::operator += (const MyClass &obj)
{
a+=obj.getA();
}
main()
{ MyClass a(1),
b(2);
a+=b;
}
Слайд 142Но есть нюанс
Некоторые операторы надо оформлять так, как надо.
Слайд 143Но есть нюанс
MyClass & MyClass::operator = (const MyClass &obj)
{// some code
here
return *this;
}
Если сделать
MyClass operator = (const MyClass &obj);
То вернется копия объекта и
a=b=c;
не будет работать.
Слайд 144Но есть нюанс
MyClass MyClass::operator + (const MyClass &obj)
{ MyClass tmp(*this);
tmp+=obj;
return tmp;
}
Если сделать
MyClass &operator + (const MyClass &obj)
то вернется копия ссылка на объект, который уничтожается при выходе из функции.
Слайд 145Но есть нюанс
MyClass MyClass::operator + (const MyClass &obj)
{ MyClass tmp(*this);
tmp+=obj;
return tmp;
}
Если сделать
return *this+=obj;
то мы поменяем себя, а этого делать не надо.
Слайд 146Но есть нюанс
MyClass MyClass::operator - () // унарный минус
{ MyClass tmp(-a);
return tmp;
}
Слайд 147Но есть нюанс
MyClass &MyClass::operator -- () // префиксный
{ a--;
return *this;
}
MyClass
&MyClass::operator -- (int) // постфиксный
{ a--;
return *this;
}
Слайд 148В итоге a += b + c - d; станет
MyClass tmp1(b);
tmp1
+= c;
MyClass tmp2(tmp1);
delete tmp1;
MyClass tmp3(tmp2);
tmp3 -= d;
MyClass tmp4(tmp3);
delete tmp3;
delete tmp2;
a += tmp4;
delete tmp4;
Слайд 149Семантика переноса
MyClass MyClass::operator + (MyClass &&obj)
{obj+=*this;
return obj;
}
Только
MyClass operator - (MyClass
&&obj)
так написать не получится. И
MyClass MyClass::operator + (const MyClass &obj)
тоже должен быть
Слайд 150В итоге a += b + c - d; станет
MyClass tmp1(b);
tmp1
+= c;
MyClass tmp3(tmp1);
tmp3 -= d;
delete tmp1;
a += tmp3;
delete tmp3;
Но мы тратим свое время на переписывание кода и даже иногда копируем его.
Зато он работает быстрее.
Слайд 151Операторы вне класса
// Этот код пишется вне класса
MyClass operator - (const
MyClass &o1,
const MyClass &o2)
{ MyClass tmp(o1);
return tmp-=o2;
}
Слайд 152Операторы вне класса
// Иногда надо сделать так
MyClass operator - (const MyClass
&o1,
const MyClass &o2)
{ MyClass tmp;
tmp.a = o1.getA() - o2.getA();
return tmp;
}
// …, но оно не будет компилироваться,
// так как нет доступа к tmp.a
Слайд 153Друзья класса
class myclass2;
class myclass1{
friend void myfunc(myclass1 c1, myclass2 c2, int val);
};
class myclass2{
friend void myfunc(myclass1 c1, myclass2 c2, int val);
};
void myfunc(myclass1 c1, myclass2 c2, int val){
// Some code
};
Слайд 154Друзья класса
«Я такой социопат и у меня нет друзей, потому что
друзья нарушают принцип инкапсуляции.»
http://bash.im/quote/405212
Слайд 155Параметры по умолчанию
C++ позволяет создавать функции, в которых часть параметров может
принимать значение по умолчанию.
Значение по умолчанию могут принимать
только последние параметры функции.
Слайд 156Параметры по умолчанию
float mypaw(float x, int p=2)
{float res=1;
for( int i=1;
i<=p; i++ )
res*=x;
return res;
}
main()
{float x, res1, res2;
…
res1 = mypow(x); // == mypow(x, 2)
res2 = mypow(x, 5);
}
Слайд 157Параметры по умолчанию
void somef(float x, int v1=1,
float v2=3.5)
{// Some code.
}
main()
{
somef(xx); // верно
somef(xx, 2); // верно
somef(xx, 3, 1.45); // верно
somef(xx, 1.45); // неверно!
}
Слайд 158Параметры по умолчанию
НАДО НАПИСАТЬ, ЧТО В КЛАССЕ ЗНАЧЕНИЕ ПО УМОЛЧАНИЮ ПИШЕТСЯ
ТОЛЬКО В ОБЪЯВЛЕНИИ, НО НЕ В ОПИСАНИИ.
Слайд 159Параметры по умолчанию
Запрещено иметь функции с одинаковыми названиями и отличающиеся лишь
…
типом параметров по умолчанию;
количеством параметров по умолчанию;
значениями параметров по умолчанию.
Слайд 160Параметры по умолчанию
Это удобно когда …
в большинстве случаев данная функция делает
одно и тоже действие (можно сэкономить на написании передаваемого параметра);
хочется визуально различать вызовы функции (например, начало работы и промежуточные стадии);
у вас уже есть масса написанного кода, а в функцию обязательно надо передать новый параметр.
Слайд 161Внимание, конкурс!
http://samag.ru/news/more/1692
Кто наберет 150 баллов и выше, получит дополнительный балл в
модуле.
Кто получит больше 75 баллов, тот получит 0,5 балла.
Не забывайте писать откуда вы!
Слайд 162LMS работает странно
Я выбираю
klyshinsky.itas.miem.edu.ru
Раздел «Программирование (5 семестр)»
Слайд 164Нет, не контрольная
Насколько понятны лекции (1-5).
Сложность семинарских занятий (1-5).
Сложность лабораторных работ
(1-5).
Что бы вы хотели услышать в рамках данного курса?
Ваши пожелания по улучшению преподавания лекций.
Ваши пожелания по семинарским занятиям и лабораторным работам.
Слайд 165Результаты опроса
Всё круто
Всё замечательно!
Всё отлично
Всё на высшем уровне
ЛЕКТОР БОГ ПРОГРАММИРОВАНИЯ
Слайд 166Результаты опроса
Рассказывать помедленнее
Говорить погромче
Более подробно объяснять материал с более наглядными примерами.
Больше
полезного материала
Больше примеров
Более основательно подходить к преподаваемому материалу, мало что воспринимается по ходу лекции
Слайд 167Результаты опроса
Интерфейсы в Qt (полный разбор конкретных задач)
Публикация лекций – FIXED
Больше
лекций, доп. занятия по С++ для тех, кто путается в основном.
(К Вострикову на лекции уже ходили?)
Ссылки на ресурсы, расширить курс до ObjectiveC (они почти одинаковые)
Слайд 168Результаты опроса
Записывать видео и выкладывать в LMS
Меньше шутить
С видео – всё
в ваших руках.
Слайд 169Результаты опроса
Если Вы спрашиваете, понятно ли нам или нет, а мы
молчим - тогда объясните нам еще раз.
Слайд 170Результаты опроса
Объяснения!!! Простите, но в аудиторию приходят не только те, кто
всё знает, но и те, кто хочет чему-то НАУЧИТЬСЯ! А если мы ничего не понимаем, как мы можем это сделать? Самообучение - единственный вариант. Но даже так сложно. :( И интерфейс нормально объясните. (ДА, МЫ ТУПЫЕ)!
Слайд 172Продолжим наши игры
В игре должны быть производственные и оборонительные здания. И
у тех, и у других, должны быть координаты на карте, прочность, список подразделений внутри, в них можно добавлять подразделения до какого-то количества, извлекать подразделения.
Подразделения делятся на транспортные и военные (возможны комбинации), военные подразделения передвигаются по суше, воде и воздуху. У всех у них есть координаты, здоровье, все они умеют рассчитывать маршрут.
Слайд 173Продолжим наши игры
Если добавлять эти свойства и методы в разные классы,
значит придется их копировать, а копирование – зло.
Что же делать?
Использовать наследование!
Слайд 174Как это делать?
class First // Базовый класс.
{public:
int a, b;
void f1();
};
class Second: First // Second наследуется от First.
{public:
double c, d;
int f2();
}; // Теперь у него есть 4 свойства и 2 метода.
Слайд 175Зачем это нужно?
Несколько классов могут быть частными случаями родительского класса (как
торговые, так и военные здания являются зданиями). Как следствие они обязаны обладать одним и тем же базовым набором свойств и поведением.
Мы гарантируем, что у всех классов-наследников, есть определенный набор методов.
Разные классы-наследники удобно считать объектами базового класса, ведь у них есть одинаковые свойства и методы.
Слайд 176Зачем это нужно (1)?
class Unit
{ public:
Unit();
Unit(int _x,
int _y, std::string _name);
~Unit();
void move();
void getPosition(int &_x, int &_y);
private:
int x, y;
std::string name;
};
Слайд 177Зачем это нужно (1)?
class TradeUnit : Unit
{ public:
TradeUnit();
TradeUnit(int _x,int _y,std::string _name);
~TradeUnit();
void loadGoods(const Goods &gd);
Goods unloadGoods(int pos);
Goods getGoods(int pos);
int getGoodsCount() {return goodsCount;}
private:
int goodsCount;
Goods *storage;
};
Слайд 178Зачем это нужно (1)?
class MilitaryUnit : Unit
{ public:
MilitaryUnit();
MilitaryUnit(int _x, int _y, std::string _name);
~MilitaryUnit();
void fight();
private:
int health;
// И еще много свойств и методов.
};
Слайд 179Интерфейс (2)
class Stream
{ public:
void operator
>>(int);
void operator <<(double);
void operator >>(double);
};
Слайд 180Интерфейс (2)
class NetworkStream : Stream
{ public:
void operator
void operator >>(int);
void operator <<(double);
void operator >>(double);
bool openConnection(std::string);
bool closeConnection();
// And so on.
};
Слайд 181Интерфейс (2)
class FileStream : Stream
{ public:
void operator
void operator >>(int);
void operator <<(double);
void operator >>(double);
bool openFile(std::string);
bool closeFile();
// And so on.
};
Слайд 182Типичные ошибки
class Point
{ public:
void setPosition(int _x, int _y);
void getPosition(int &_x, int &_y);
private:
int x, y;
};
class Unit : Point
{ …};
Теперь можно сделать unit1.setPosition(-1, -1), то есть телепортировать юнит.
Слайд 183Типичные ошибки
При проектировании структуры классов можно выделить общие фрагменты в классах
и реализовать их один раз в родительском классе.
Но обычно это дурной тон!
Следует различать наследование и владение.
Слайд 184Типичные ошибки
class Unit
{ public:
Unit(int _x, int _y, std::string _name);
void move();
private:
Point pt;
};
В данном случае подразделение двигается как это описано в логике его поведения.
Слайд 185Инициализация объектов
Unit::Unit()
{x=0; y=0; name=“”;}
Unit::Unit(int _x, int _y, std::string _name)
{x=_x; y=_y; name=_name;}
MilitaryUnit::MilitaryUnit()
{
health=0; }
MilitaryUnit::MilitaryUnit(int _x, int _y, std::string _name)
{x=_x; y=_y; name=_name; health=0;}
Слайд 186Инициализация объектов
При вызове конструктора сперва вызываются конструкторы базовых классов и лишь
после них конструктор данного класса.
MilitaryUnit mu1;
Превращается в
mu1.Unit::Unit() {x=0; y=0; name=“”;}
mu1.MilitaryUnit::MilitaryUnit()
{ health=0; }
Слайд 187Инициализация объектов
При вызове конструктора сперва вызываются конструкторы базовых классов и лишь
после них конструктор данного класса.
MilitaryUnit mu1(10, 10, “unit1”);
Превращается в
mu1.Unit::Unit() {x=0; y=0; name=“”;}
mu1.MilitaryUnit::MilitaryUnit(10, 10, “unit1”)
{x=10; y=10; name=“unit1”; health=0;}
Слайд 188Инициализация объектов
Unit::Unit()
{x=0; y=0; name=“”;}
Unit::Unit(int _x, int _y, std::string _name)
{x=_x; y=_y; name=_name;}
MilitaryUnit::MilitaryUnit()
{
health=0; }
MilitaryUnit::MilitaryUnit(int _x, int _y, std::string _name)
: Unit(_x, _y, _name)
{ health=0; }
Слайд 189Инициализация объектов
MilitaryUnit mu1(10, 10, “unit1”);
Превращается в
mu1.Unit::Unit(){x=0; y=0; name=“”;}
mu1.MilitaryUnit::MilitaryUnit(10, 10, “unit1”)
{ health=0; }
Слайд 190Инициализация объектов
Unit::Unit()
{x=0; y=0; name=“”;}
Unit::Unit(int _x, int _y, std::string _name)
{x=_x; y=_y; name=_name;}
MilitaryUnit::MilitaryUnit()
{ init(); }
MilitaryUnit::MilitaryUnit(int _x, int _y, std::string _name)
: Unit(_x, _y, _name) { init(); }
void MilitaryUnit::init()
{ health=0; } // Так лучше
Слайд 191Зоны видимости
Иногда необходимо сделать так, чтобы даже наследник не имел доступа
к части данных. Для этого используется private.
Если мы хотим позволить дочернему классу иметь доступ к данным родителей, нам следует использовать модификатор доступа protected.
Слайд 192Зоны видимости
Также можно изменять вид наследования:
private или public.
По умолчанию –
private.
Слайд 193Зоны видимости
class MilitaryUnit : public Unit // Так правильнее
{ public:
MilitaryUnit();
MilitaryUnit(int _x, int _y, std::string _name);
~MilitaryUnit();
void fight();
private:
int health;
// И еще много свойств и методов.
};
Слайд 194Перегрузка функций
void Unit::move()
{// Просто юнит не передвигается!
}
void MilitaryUnit::move()
{// Поведение войск
несколько иное.
switch(orders)
{ case guard: // Stay here
// And so on
}
}
Слайд 195Перегрузка функций
main()
{
Unit unit;
MilitaryUnit *munit=new MilitaryUnit;
munit->move(); // Корректно перемещает
юнит.
unit.move(); // Ничего не делает.
((Unit*)munit)->move(); // Ничего не делает.
// ^^^^^ Это корректное преобразование типов.
}
Слайд 196Виртуальные функции
Предположим, что мы создали несколько классов на основе класса Unit.
Unit
MilitaryUnit
TradeUnit
CivilUnit
Battleship
Galley
Infantry
Chivalry
Cart
Clipper
sluggard
Слайд 197Виртуальные функции
class TheGame
{
private:
// Мы ведь хотим, чтобы каждый объект вел
себя
// корректно, а для этого надо знать тип.
Battleship *btlships;
int bshipCount;
Chivalry *chivs;
int ChCount;
// И так сто раз.
}
Слайд 198Ах, как было бы здорово, …
… если бы каждый объект знал,
какого он типа, и вызывал правильную функцию move() даже после преобразования типов. Тогда всех можно было бы хранить как Unit*.
Слайд 199Есть такая партия!
Это виртуальные функции.
class Unit
{
// some code
virtual void
move();
// more code
};
Слайд 200Виртуальные функции
class MilitaryUnit : public Unit
{ // …
virtual void
move(); // Будет сделана реализация
};
class Chivalry : public MilitaryUnit
{ // …
virtual void move(); // Везде надо писать virtual
};
Слайд 201Виртуальные функции
main()
{
Unit unit;
Chivalry *chiv=new Chivalry;
chiv->move(); // Корректно перемещает
юнит.
unit.move(); // Ничего не делает.
((Unit*) chiv)->move(); // Всё равно перемещает.
// ^^^^^ Это корректное преобразование типов.
((Unit*) chiv)->Chivalry::move(); // Всё равно перемещает.
}
Слайд 202Виртуальные функции
class TheGame
{
void moveAll();
private:
// Сейчас для каждого объекта будет
вызываться
// правильный move.
Unit **allUnits; // Именно массив указателей.
int unitsCount;
}
Слайд 203Виртуальные функции
void TheGame::moveAll()
{
for( int i=0; imove();
// Сами разберутся
}
Слайд 204Абстрактные функции
На самом деле не существует такого объекта, как Unit. Ну
или не должно существовать. Хорошо бы запретить создание таких объектов.
Абстрактная функция, это функция, у которой нет реализации.
Абстрактный класс, это класс, у которого есть абстрактные функции.
Создавать объекты абстрактных классов запрещено.
Слайд 205Более правильное решение
enum UnitKind={ukNone,ukChivalry,ukGalley /*, …*/};
class Unit
{
public:
UnitKind getKind()
{ return kind; };
virtual void move()=0;
private:
UnitKind kind;
};
// Наследуем от Unit класс MilitaryUnit, а от него
// класс Chivalry и остальные
Слайд 206Более правильное решение
Chivalry::Chivalry()
{// …
kind=ukChivalry;
}
Galley::Galley()
{// …
kind=ukGalley;
}
Слайд 207Новые возможности
В 11-м стандарте С++ добавлены две новых возможности: override и
final.
override применяется при перегрузке виртуальных функций.
final показывает, что наследование от класса или перегрузка функции запрещены.
Слайд 208Пример
struct B
{virtual void some_func();
virtual void f(int);
virtual void g() const;
};
struct
D1 : public B
{void sone_func() override; // неверное имя функции
void f(int) override; // OK
virtual void f(long) override; // несоответствие типа
virtual int f(int) override;//несоответствие возв. типа
virtual void g() const final; // OK
virtual void g(long); // OK: новая виртуальная функция
};
struct D2 : D1
{virtual void g() const; // замещение финальной функции
};
Слайд 209Пример 2
struct F final
{
int x, y;
};
struct D :
F // ошибка: наследование от final классов запрещено
{
int z;
};
Слайд 211Пространство имен
file1.hpp
int value(){ return 5;}
file2.hpp
const double pi=3.1416
int value(){ return 2*pi;}
file3.cpp
#include
#include
int i=value(); // И что вызовется?
Слайд 212Пространство имен
file1.hpp
namespace space1
{
int value(){ return 5;}
}
file2.hpp
namespace space2
{
const double pi=3.1416
int value(){ return 2*pi;}
}
Слайд 213Пространство имен
file3.cpp
#include
#include
int i=space1::value(); // file1.hpp
int j=space2::value(); // file2.hpp
Слайд 214Пространство имен
file3.cpp
#include
#include
using namespace space1;
int i=value(); // file1.hpp
using namespace space2;
int
j=space2::value(); // file2.hpp
// Без space2:: была бы ошибка, так как опять непонятно кого вызывать.
Слайд 216Консольный ввод/вывод
Данные классы предназначены для работы с консолью – специальными потоками
данных, перенаправляемых с клавиатуры или в окно консольной программы.
Слайд 217Класс istream
Форматированный ввод
istream& operator>> (bool& val);
istream& operator>> (short& val);
istream&
operator>> (unsigned short& val);
istream& operator>> (int& val);
istream& operator>> (unsigned int& val);
istream& operator>> (long& val);
istream& operator>> (unsigned long& val);
istream& operator>> (long long& val);
istream& operator>> (unsigned long long& val);
istream& operator>> (float& val);
istream& operator>> (double& val);
istream& operator>> (long double& val);
istream& operator>> (void*& val);
Слайд 218Класс istream
Неформатированный ввод
single character
int get();
istream& get (char& c);
c-string
istream& get (char*
s, streamsize n);
istream& get (char* s, streamsize n, char delim);
istream& getline(char* s, streamsize n);
istream& getline(char* s, streamsize n, char delim);
Слайд 219Класс ostream
Форматированный вывод
ostream& operator
short val);
ostream& operator<< (int val);
ostream& operator<< (unsigned int val);
ostream& operator<< (long val);
ostream& operator<< (unsigned long val);
ostream& operator<< (long long val);
ostream& operator<< (unsigned long long val);
ostream& operator<< (float val);
ostream& operator<< (double val);
ostream& operator<< (long double val);
ostream& operator<< (void* val);
Слайд 220Класс istream
Неформатированный ввод
single character
istream& put (char& c);
c-string
Только
Слайд 221Класс istream
Для программы определяются следующие потоки.
cin – консольный ввод (клавиатура).
cout –
консольный вывод (черное окно).
cerr – поток ошибок (окно, в которое выдаются ошибки компилятора).
clog – поток журнала (окно, в которое выдаются сообщения о запуске приложения, потоков, загрузке/выгрузке библиотек, …).
Слайд 222Класс istream
// istream::getline example
#include // std::cin, std::cout
int main
() {
char name[256], title[256];
std::cout << "Please, enter your name:";
std::cin.getline (name,256);
std::cout << «Enter your favorite movie:";
std::cin.getline (title,256);
std::cout << name << "'s favorite movie is " << title;
return 0;
}
Слайд 223Класс ifstream
Конструкторы
ifstream();
explicit ifstream (const char* filename, ios_base::openmode mode = ios_base::in);
explicit ifstream
(const string& filename, ios_base::openmode mode = ios_base::in);
ifstream (const ifstream&) = delete;
ifstream (ifstream&& x);
Слайд 224explicit
Чтобы предотвратить неявные преобразования параметров, передаваемых в конструктор, можно добавить в
объявление ключевое слово explicit. Это заставляет код использовать параметр правильного типа или привести параметр к правильному типу. То есть, если приведение не выражено в коде явно, возникнет ошибка.
Слайд 225explicit
Заодно приводит следующий код к вызову правильного конструктора.
class cl2
{cl2(int); // Нет
конструктора по умолчанию
…
};
cl2 v=2;
//Не создает, а потом инициализирует,
// а вызывает правильный конструктор.
Слайд 226Класс ifstream
Режимы открытия файлов
Слайд 227Класс ifstream
Открытие/закрытие
void open (const char* filename, ios_base::openmode mode = ios_base::in);
void
open (const string& filename, ios_base::openmode mode = ios_base::in);
void close();
bool good() const;
bool eof() const;
Слайд 228Класс ifstream/ofstream
Чтение двоичных данных
istream& read (char* s, streamsize n);
Запись двоичных данных
ostream&
write (const char* s, streamsize n);
Слайд 229Класс ifstream
Позиционирование
istream& seekg (streampos pos);
istream& seekg (streamoff off, ios_base::seekdir way);
ios_base::beg beginning of
the stream
ios_base::cur current position in the stream
ios_base::end end of the stream
streampos tellg(); // текущая позиция
Слайд 230Класс ifstream (пример)
// read a file into memory
#include
// std::cout
#include // std::ifstream
int main () {
std::ifstream is ("test.txt", std::ifstream::binary);
if (is) {
is.seekg (0, is.end);
int length = is.tellg();
is.seekg (0, is.beg);
Слайд 231Класс ifstream (пример)
char * buffer = new char [length];
is.read (buffer,length);
is.close();
std::cout.write (buffer,length);
delete[] buffer;
}
return 0;
}
Слайд 232Класс ifstream (пример)
int *buffer=
new
char [length/sizeof(int)];
is.read ((char*)buffer,length);
is.close();
…
delete [] buffer;
}
return 0;
}
Слайд 233Сериализация объектов
class Unit
{
UnitKind getKind() { return kind; };
virtual void
write(ofstream &file)=0;
virtual void read(ifstream &file)=0;
virtual void move()=0;
private:
UnitKind kind;
};
Слайд 234Сериализация объектов
class Infantry: Unit
{
virtual void write(ofstream &file);
virtual void read(ifstream
&file);
virtual void move();
private:
float shield, force, health;
};
Слайд 235Сериализация объектов
void Infantry::write(ofstream &file)
{
file
force
<< ”, ” << health << ”\n”;
};
// Текстовый случай
void Infantry::read(ifstream &file)
{char c;
file >> shield >> c >> force >> c
>> health >> c;
};
Слайд 236Сериализация объектов
void Infantry::write(ofstream &file)
{
file.write((char*)&shield,sizeof(float));
file.write((char*)&force,sizeof(float));
file.write((char*)&health,sizeof(float));
};
// Бинарный случай (совпадение имен!)
void Infantry::read(ifstream &file)
{file.read((char*)&shield,sizeof(float));
file.read((char*)&force,sizeof(float));
file.read((char*)&health,sizeof(float));
};
Слайд 237Класс ifstream
class UnitCollection
{
…
void write(std::string filename);
void read (std::string filename);
…
};
Слайд 238Класс ifstream
… ::write(std::string filename)
{std::ofstream ofile(filename);
int tmp;
ofile.write((char*)&ucnt, sizeof(int));
for( int
i=0; i {tmp=units[i]->getKind()
ofile.write((char*)&tmp, sizeof(int));
units[i]->write(ofile);
}
}
Слайд 239Класс ifstream
… ::read(std::string filename)
{std::ifstream ifile(filename);
int tmp;
ifile.read((char*)&ucnt, sizeof(int));
units=new Unit*[ucnt];
for( int i=0; i {ifile.read((char*)&tmp, sizeof(int));
if(tmp==utInf) units[i]=new Infantry;
else if(tmp==utChiv) units=new Chival;
…
units[i]->read(ifile);
} }
Слайд 240Класс ifstream
ifstream &operator >> (ifstream &file, Infantry &data)
{data.read(file);
return file;
}
ofstream &operator
<< (ofstream &file, Infantry &data)
{data.write(file);
return file;
}
Слайд 241Класс ifstream
ifstream &operator >> (ifstream &file, Infantry &data)
{data.read(file);
return file;
}
ofstream &operator
<< (ifstream &file, Infantry &data)
{data.write(file);
return file;
}
Слайд 243Что нас ждет в конце модуля?
Две лабораторные работы (1 и 2
балла соотв.):
использование контейнеров STL в консоли;
разработка собственного контейнера и работа с ним в оконном приложении.
Курсовик (2 балла):
постановка задачи, правила работы (игры),
метод решения задачи ((выигрышная) стратегия),
фрагменты кода.
Зачет: письменная работа на 1,5 часа (5 баллов).
Слайд 244Разбор полетов, мысли
В любом случае, вам необходимо больше работать самостоятельно.
Это слишком
сложно?
http://www.youtube.com/watch?v=KkMDCCdjyW8
Самое интересное начинается после 5:30.
Слайд 245Разбор полетов, мысли
В любом случае, вам необходимо больше работать самостоятельно.
Это слишком
сложно?
http://www.cse.unt.edu/~rada/CSCE5290/
Слайд 246Перспективы
Все пересдачи после зимней сессии.
Если Вы не сдали как зачет, так
и экзамен – это только один долг.
Если Вы не сдали экзамен и Вас за это не отчислили, то (¡кажется, т.е. до подтверждения учебным офисом это личное мнение лектора!) Вам придется прослушать этот курс еще раз за дополнительные деньги.
Слайд 247Перспективы
Лабораторные работы:
Сериализация (чтение/запись файлов с использованием библиотеки STL) – переделать имеющийся
у вас контейнер так, чтобы он имел возможность сохранять информацию в файл и считывать ее оттуда. (1 балл)
Хранение произвольных типов – переделать имеющийся контейнер с использованием шаблонов () и с применением операторов (присвоение, сравнение, …). (1 балл)
400 строк кода на человека по выбранной теме. (3 балла)
Слайд 248Перспективы
Экзамен в письменной форме (5 баллов). Задания аналогичны тем, что были
на зачеты, но требования несколько выше.
Лица, не сдавшие все лабораторные работы, не допускаются до экзамена.
Если сумма двух оценок до округления ниже 4 баллов, округление производится в меньшую сторону (итоговой оценкой за предмет является «неудовлетворительно»). В противном случае применяется арифметическое округление.
Слайд 250Лекция 7
Статические члены класса, обработка исключений
Слайд 251Что делать, если нужна глобальная переменная?
Статический член класса создается в единственном
экземпляре для всех объектов данного класса или классов-наследников.
Фактически он является глобальной переменной, но относится именно к данному классу.
Слайд 252Пример
В программе мы динамически выделяем и освобождаем массу объектов. Но судя
по всему у нас имеет место утечка памяти. Для начала хочется понять, объекты каких классов мы забываем удалить.
Для этого заведем в классе статическую переменную, которую будем увеличивать при создании объекта и уменьшать при уничтожении.
Слайд 253Объявление статических переменных
// ClassWithCounter.h
class ClassWithCounter
{public:
ClassWithCounter();
~ClassWithCounter();
private:
static int
cntr;
};
Слайд 254Объявление статических переменных
// ClassWithCounter.cpp
int ClassWithCounter::cntr=0;
ClassWithCounter::ClassWithCounter()
{cntr++;
}
ClassWithCounter::~ClassWithCounter()
{cntr--;
}
Слайд 256Объявление
статической переменной
Фактически при объявлении класса мы обещаем, что заведем глобальную
переменную, но логически она будет относиться к данному классу.
Дальше при описании класса мы не забываем ее на самом деле объявить и инициализировать.
Вот так появляется единственная переменная на все объекты данного класса. Что бы вы об этом ни думали и что бы ни имели в виду при создании.
Слайд 257Кстати об инициализации
// ClassWithCounter.cpp
// Инициализация переменной
int ClassWithCounter::cntr=0;
ClassWithCounter::ClassWithCounter()
{cntr=0; // Так поступать
глупо.
cntr++;
}
Слайд 258Статические методы
В отличие от переменных, статические методы, это методы, которые не
обращаются к членам класса и не вызывают методов класса за исключением статических (членов и методов).
За счет этого статические методы могут вызываться без создания объекта.
Слайд 259Пример
// ClassWithCounter.h
class ClassWithCounter
{public:
ClassWithCounter();
~ClassWithCounter();
static int getCounter(){ return cntr;}
private:
static int cntr;
};
Слайд 260Пример 2
class Complex
{public:
Complex();
Complex(double r, double i);
…
static Complex
zero()
{return Complex(0, 0);}
…
};
Слайд 261Пример 3
QString Static Public Members
Слайд 262Отслеживание ошибок
Для проверки корректности данных может использоваться функция std::assert(int).
Если переданное в
нее значение равно 0, функция вызывает прекращение работы программы и выдает сообщение вида.
Assertion failed: expression, file filename, line line number
В противном случае программа продолжает работать.
Функция вызывается только при компиляции проекта с отладочной информацией.
Слайд 263Например
#include
void print_number(int* myInt)
{assert (myInt!=NULL);
printf ("%d\n",*myInt);
}
int main ()
{int a=10,
*b = NULL, *c = NULL;
b=&a;
print_number (b); print_number (c);
return 0;
}
Слайд 264Например 2
class Storage
{private:
int *data; //Данные
int count; // И их
количество
public:
int operator[](int index)
{// Кругом враги.
assert(index>=0 && index return data[index];
}
Слайд 265Исключительные ситуации
В С++ предусмотрен еще один метод обработки ошибок – обработка
исключений.
try
{ // Проблемный код.
}
catch(...)
{// Код, обрабатывающий проблему.
}
Слайд 266Исключительные ситуации
Блок try содержит в себе код, порождающий исключения: деление на
ноль, некорректное обращение к памяти, …
При возникновении исключения управление передается в блок catch, который производит действия по устранению ошибки.
Как в случае корректного выполнения программы, так и при порождении исключения, далее программа продолжает выполнение с оператора, следующего за catch( ){ }.
Слайд 267Например
int f1(int a)
{ return a/0; // }:-] Here comes Ktulhu!
int
f2()
{ return f1(10); }
void f3()
{int res;
try
{res=f2();}
catch(...)
{res=0;}
return res;
}
Слайд 268Исключительные ситуации
В круглых скобках у catch можно указать тип (в смысле
типов данных) исключения.
Например, в библиотеке STL определен базовый класс исключения exception, от которого наследуются другие типы.
Слайд 269Порождение исключений
Для порождения исключений используется оператор throw.
throw объект или значение;
Слайд 270Например 2
using namespace std;
int main()
{try
{throw range_error("The range error!");
}
catch (exception &e)
{cerr<<"Caught: "< cerr<<"Type:"< }
}
Слайд 271Например 3
using namespace std;
int main()
{try
{int i=random()*100;
throw i;
}
catch (int a)
{cerr<<"Caught: "<< a << endl;
}
}
Слайд 272Например 4
class Trace {
static int counter;
int objid;
public:
Trace(){objid = counter++;
cout<<"СTrace #"< if(objid == 3) throw 3; }
~Trace(){cout<<"DTrace #"<};
Слайд 273Например 4
int Trace::counter = 0;
int main() {
try {
Trace n1;
Trace array[5];
Trace n2; // Won't get here.
} catch(int i) {
cout << "caught " << i << endl;
}
}
Слайд 274Исключительные ситуации
В разных библиотеках определены собственные типы исключений.
Qt – QException
Builder /
Delphi – Exception
Visual Studio – CException
И так далее.
Слайд 275Исключительные ситуации STL
В STL определены функции signal и reise.
Функция reise порождает
сигнал о исключении.
Функция signal позволяет завести обработчик подобного исключения
Слайд 276Например
#include
long prev_fact, i;
void SIGhandler(int sig)
{printf("\nReceived a SIGUSR1. The
answer is %ld! = %ld\n",i-1, prev_fact);
exit(0);
}
Слайд 277Например
void main(void)
{long fact;
printf("Factorial Computation:\n\n");
signal(SIGUSR1, SIGhandler);
for(prev_fact=i=1; ;i++,prev_fact=fact)
{fact = prev_fact * i;
if (fact < 0) raise(SIGUSR1);
else if (i % 3 == 0)
printf(" %ld! = %ld\n", i, fact);
}
}
Слайд 278Исключительные ситуации
Не все аппаратные платформы поддерживают генерацию исключительных ситуаций.
Не все ОС
поддерживают генерацию и обработку исключений.
В разных языках с исключениями всё по-разному.
В разных библиотеках обработка исключений реализована различным и своим собственным образом.
Следовательно, при разработке с применением разных языков/компиляторов/платформ исключения применяться НЕ МОГУТ.
Слайд 280Проблема
Копировать код – зло.
Мне надо написать несколько функций меняющих значения двух
переменных для разных типов.
Копировать?
Слайд 281Пусть будет «Нет» и я хитр
void swap(Infantry &a, Infantry &b)
{Infantry tmp=a;
a=b;
b=tmp;
}
void swap(int &a, int &b)
{b+=a;
a=b-a;
b-=a;
}
Слайд 282Шаблонные функции
Если имеется несколько функций, совершающих одни и те же действия
для разных типов, используются шаблонные (template) функции, позволяющие отложить определение типа до момента использования функции.
Слайд 283«Нет» и я
образован
template
void swap(T &a, T &b)
{T tmp=a;
a=b;
b=tmp;
}
Слайд 284«Нет» и я
современно образован
template
void swap(SwapType &a, SwapType &b)
{SwapType
tmp=a;
a=b;
b=tmp;
}
// Ключевые слова class и typename здесь
// взаимозаменяемы.
// Имя типа – почти любое
Слайд 285«Нет» и я образован, хитер
и невнимателен
template
void swap(T &a,
T &b)
{b+=a;
a=b-a;
b-=a;
}
// Подсказка: как сложить и вычесть
// двух студентов?
Слайд 286Вызов шаблонной функции
int i[5] = { 5, 4, 3, 2, 1
};
sort< int >( i, 5 );
char c[] = "бвгда";
sort< char >( c, strlen( c ) );
Слайд 287Вызов шаблонной функции
// Компилятор может сам определить тип.
int i[5] = {
5, 4, 3, 2, 1 };
// вызывается sort< int >
sort( i, i + 5 );
char c[] = "бвгда";
// вызывается sort< char >
sort( c, c + strlen( c ) );
Слайд 288Параметры шаблонов
template
void sort(T &data[size])
{for( int i=0; i
i++ )
for( int j=i+1; j if( data[i] swap(data[i], data[j] );
}
Слайд 289Типичные ошибки
У используемого типа должны быть все члены класса и методы
класса, используемые в шаблонной функции.
Пусть у класса Unit нет операторов сравнения.
template
T min(const T &a, const T &b)
{ return aне будет работать для
Unit c, d, e;
e=min(a, b);
Слайд 290Типичные ошибки
Не должно возникать противоречий между типами параметров.
// ошибка: параметр int[]
а не char[]
sort< int >( c, 5 );
// ошибка: несоответствие типов.
int a; Unit b;
swap(a, b);
Слайд 291Типичные ошибки
Вызов шаблонной функции не должен приводить к созданию функции с
тем же именем и набором параметров (в том числе параметров по умолчанию).
void swap(int a, int b, bool how=false);
template
void swap(T a, T b);
int c, d;
swap(c, d); // Кого вызывать?
Слайд 292Несколько типов шаблонов
template
T1 min(T1 *data1,T2 *data2, int
size2)
{T1 mn=data1[data2[0]];
for( int i=1; i if( mn>data1[data2[i]] )
mn=data1[data2[i]];
return mn;
}
Слайд 293Шаблонные классы
template
class MyContainer
{public:
…
T operator[](int index);
void sort();
…
protected:
T *data;
int count;
};
Слайд 294Функции шаблонных классов
template
void MyContainer::sort()
{…}
template
T MyContainer::operator[](int index)
{…}
Слайд 295Переменные шаблонных классов
MyContainer int_coll;
MyContainer unit_coll;
typedef MyContainer PUnitCollect;
PUnitCollect unit_coll2;
MyContainer u_c3;
Слайд 296Несколько типов шаблонов
template
T2 min(T1 &data1)
{T2 mn=data1[0];
for(
int i=1; i if( mn>data1[i] )
mn=data1[i];
return mn;
}
Слайд 297Шаблонные классы
template
class MyContainer
{ …
};
template
size>
MyContainer::grow()
{T tmp=new T[count+size];
…
}
Слайд 298Наследование от шаблонов
template
class MyCollection2 : public MyCollection
{…
};
Слайд 299Особенности работы
с шаблонами
Для каждой новой функции или класса компилятор пытается
сгенерировать новый код, заменяя все шаблонные типы на конкретные значения. Из этого следует что:
1. сократить объем исполняемого файла за счет шаблонов не получится;
2. весь шаблонный код должен быть доступен в момент компиляции (располагаться выше или в хидерах);
3. создаются только используемые функции.
Слайд 300Учет переменных по-новому
template
class Counter
{public:
Counter(){cnt++;}
~Counter(){cnt--;}
int getCount(){return cnt;}
protected:
static int cnt;
};
Слайд 301Учет переменных по-новому
class A
{public:
Counter cntr;
// some other stuff.
};
class B
{public:
Counter
cntr;
// some other stuff.
};
Слайд 302Учет переменных по-новому
Так как Counter и Counter - это два разных
типа, то и счетчики в них будут заведены собственные.
При этом надо не забыть проинициализировать статические переменные.
static int Counter
::cnt=0;
static int Counter::cnt=0;
Слайд 303Определения типов внутри шаблонного класса
template
class MyContainer
{public:
typedef T* iterator;
…
protected:
T *data;
int count;
};
Слайд 304Параметр-шаблон
template< class Type, template< class > class Container >
class CrossReferences
{
Container< Type > mems;
Container< Type* > refs;
...
};
CrossReferences< Date, vector > cr1;
CrossReferences< string, set > cr2;
Слайд 305Сложный пример
template< class T >
T* CreateArray( T(*GetValue)(), const int size
)
{
T *Array = new T[ size ];
for( int i = 0; i < size; i++ )
Array[i] = GetValue();
return Array;
}
Слайд 306Сложный пример
int GetZero() { return 0; }
char InputChar()
{
char c;
cin >> c;
return c;
}
int*ArrayOfZeros=CreateArray(GetZero,20);
char *String=CreateArray(InputChar,40);
Слайд 307Типы как члены класса
class Container
{public:
int array[ 15 ];
typedef int*
iterator;
…
iterator begin() { return array; }
};
Слайд 308Типы как члены класса
template< class C >
void f( C& vector )
{
C::iterator i = vector.begin(); // ошибка
typename C::iterator i = vector.begin();
}
Слайд 309Почему это вызывает ошибку?
template void f(){T::x * p;...}
Выражение T::x *
p может означать две вещи. Первая: x - это имя некого типа, а все выражение есть объявление указателя p. Вторая: x - это статическая переменная, а * - это знак умножения. Тогда p - это тоже какая-то переменная.
Ключевое слово typename разрешит это недоразумение, явно сказав компилятору, что речь идет о типе.
template void f() { typename T::x * p; ... }
// http://alenacpp.blogspot.ru/2006/08/typename.html
Слайд 310Типы как члены класса (развитие идеи)
class Container
{public:
int
array[ size ];
typedef T* iterator;
…
iterator begin() { return array; }
};
Слайд 311А теперь выучим новое слово
Mutable – ключевое слово означающее, что эту
переменную можно менять в константной функции.
Слайд 312Например
class CFoo
{int cachedValue;
bool bCached;
public:
int calculate() const
{//долгое вычисление
cachedValue = ...; //ошибка:
// нельзя присваивать данным класса
// в константной функции
}
};
Слайд 313Например
class CFoo
{mutable int cachedValue;
mutable bool bCached;
public:
int calculate() const
{if(bCached)
return cachedValue;
//долгое вычисление
cachedValue = ...; //все в порядке
bCached = true;
}
}; // http://alenacpp.blogspot.ru/2005/10/mutable-constcast.html
Слайд 314Лекция 8
Параллельное программирование
Слайд 315Проблема
Когда я нажимаю на кнопку, которая выполняет сложные вычисления, весь интерфейс
замирает и ничего не делает. Даже просто становится белым. И ничего не работает пока вычисления не закончатся.
Слайд 317Как мы думали оно работает
Очередь сообщений
ОС
Цикл обработки сообщений
Функция1
Функция2
ФункцияN
Слайд 318Как мы думали оно работает
Очередь сообщений
ОС
Цикл обработки сообщений
Функция1
Функция2
ФункцияN
Генерируется сообщение
Слайд 319Как мы думали оно работает
Очередь сообщений
ОС
Цикл обработки сообщений
Функция1
Функция2
ФункцияN
Цикл получает сообщение и
начинает обработку
Слайд 320Как мы думали оно работает
Очередь сообщений
ОС
Цикл обработки сообщений
Функция1
Функция2
ФункцияN
Цикл получает сообщение и
вызывает функцию1
Слайд 321Как мы думали оно работает
Очередь сообщений
ОС
Цикл обработки сообщений
Функция1
Функция2
ФункцияN
Функция1 отрабатывает и возвращает
управление
Слайд 322Как мы думали оно работает
Очередь сообщений
ОС
Цикл обработки сообщений
Функция1
Функция2
ФункцияN
Приходит следующее сообщение
Слайд 323Откуда взялась проблема
Очередь сообщений
ОС
Цикл обработки сообщений
Функция1
Функция2
ФункцияN
Слайд 324Откуда взялась проблема
Очередь сообщений
ОС
Цикл обработки сообщений
Функция1
Функция2
ФункцияN
Генерируется сообщение
Слайд 325Откуда взялась проблема
Очередь сообщений
ОС
Цикл обработки сообщений
Функция1
Функция2
ФункцияN
Цикл получает сообщение и начинает обработку
Слайд 326Откуда взялась проблема
Очередь сообщений
ОС
Цикл обработки сообщений
Функция1
Функция2
ФункцияN
Цикл получает сообщение и вызывает функцию
Слайд 327Откуда взялась проблема
Очередь сообщений
ОС
Цикл обработки сообщений
Функция1
Функция2
ФункцияN
Приходит новое сообщение, цикл занят
Слайд 328Откуда взялась проблема
Очередь сообщений
ОС
Цикл обработки сообщений
Функция1
Функция2
ФункцияN
Цикл занят, так как точка выполнения
в функции1.
Слайд 329Решение
Очередь сообщений
ОС
Цикл обработки сообщений
Функция1
Функция2
ФункцияN
Поток 1
Поток 2
Поток 3
Слайд 330Решение (на самом деле)
Очередь сообщений
ОС
Цикл обработки сообщений (ЦОС)
Функция1
Функция2
ФункцияN
Поток 1
Поток 2
Поток 3
ЦОС
ЦОС
Слайд 331Решение
Очередь сообщений
ОС
Цикл обработки сообщений
Функция1
Функция2
ФункцияN
Поток 1
Поток 2
Поток 3
Генерируется сообщение
Слайд 332Решение
Очередь сообщений
ОС
Цикл обработки сообщений
Функция1
Функция2
ФункцияN
Поток 1
Поток 2
Поток 3
Слайд 333Решение
Очередь сообщений
ОС
Цикл обработки сообщений
Функция1
Функция2
ФункцияN
Поток 1
Поток 2
Поток 3
Слайд 334Решение
Очередь сообщений
ОС
Цикл обработки сообщений
Функция1
Функция2
ФункцияN
Поток 1
Поток 2
Поток 3
Слайд 335Решение
Очередь сообщений
ОС
Цикл обработки сообщений
Функция1
Функция2
ФункцияN
Поток 1
Поток 2
Поток 3
Слайд 336Решение
Очередь сообщений
ОС
Цикл обработки сообщений
Функция1
Функция2
ФункцияN
Поток 1
Поток 2
Поток 3
Слайд 337Что для этого есть в Qt?
class WorkerThread : public QThread
{Q_OBJECT
void run() Q_DECL_OVERRIDE {
/* expensive or blocking operation */
}
};
void MainWindow::on_pushButton_clicked()
{WorkerThread *thrd=new WorkerThread;
thrd.start()
}
Слайд 338Почему это плохо?
class WorkerThread : public QThread
// Наследование лучше оставить для
// расширения возможностей класса.
// Также не будет работать останов.
void MainWindow::on_pushButton_clicked()
{WorkerThread *thrd=new WorkerThread;
thrd.start()
}
// Утечка памяти, *thrd не хранится.
Слайд 339Класс QThread
public:
void exit(int returnCode = 0);
// Tells the thread's event loop
to exit
// with a return code.
bool wait(unsigned long time=ULONG_MAX);
// Blocks the thread for time msec.
Слайд 340Класс QThread
public slots:
void quit();
void start();
void terminate();
signals:
void finished();
void started();
void terminated();
Слайд 341Как сделать лучше
class Worker
{public slots:
void doWork();
};
void MainWindow::on_pushButton_clicked()
{QThread *thread =
new QThread;
Worker *worker = new Worker;
connect(thread, SIGNAL(started()), worker, SLOT(doWork()));
thread->start();
}
Слайд 342Как сделать еще лучше
class Worker
{public:
QThread *thrd;
public slots:
void
doWork();
void stop(){thrd->quit();delete thrd;}
signals:
void finished();
};
Слайд 343Как сделать еще лучше
Worker *worker=NULL;
void MainWindow::on_pushButton_clicked()
{QThread *thread = new QThread;
worker
= new Worker;
connect(thread, SIGNAL(started()), worker, SLOT(doWork()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
worker->thrd = thread;
thread->start();
}
Слайд 344Как сделать еще лучше
void MainWindow::on_pushButton2_clicked()
{
if( worker )
{worker->stop();
delete worker;
worker=NULL;
}
}
Слайд 345Возможные проблемы
Одновременное обращение к одной и той же ячейке памяти (независимо
на чтение или запись) приводит к прекращению работы программы.
То есть надо
а) запретить запуск нескольких потоков, или
б) обеспечить каждый поток своими данными (или их копией), или
в) синхронизировать работу потоков.
Слайд 346Мьютексы
Операционная система позволяет проводить синхронизацию работы различных потоков при помощи специальных
структур данных, например, мьютексов.
Мьютекс находится в одном из двух состояний: свободен или заблокирован. Если мьютекс заблокирован, то попытка его блокировки приводит к остановке выполнения потока до разблокировки мьютекса.
Слайд 347QMutex
public:
void lock();
bool tryLock();
bool tryLock(int timeout);
void unlock();
Слайд 348Например
Поток1
Поток2
ПотокN
Результаты
Слайд 349QMutex
ResType *lst=NULL; int resCount=0;
QMutex locker;
void produceResults()
{locker.lock()
ResType *tmp=new ResType[resCount+1];
for(int i=0;i
tmp[resCount]=myres;
delete [] lst; lst=tmp; resCount++;
locker.unlock();
}
Слайд 350Например
ПОТОК 1
produceResults()
{locker.lock()
new
for(…);
tmp[]=myres;
изменения;
locker.unlock();
}
Слайд 351Например
ПОТОК 1
produceResults()
{locker.lock()
new
for(…);
tmp[]=myres;
изменения;
locker.unlock();
}
Слайд 352Например
ПОТОК 1
produceResults()
{locker.lock()
new
for(…);
tmp[]=myres;
изменения;
locker.unlock();
}
Слайд 353Например
ПОТОК 1
produceResults()
{locker.lock()
new
for(…);
tmp[]=myres;
изменения;
locker.unlock();
}
ПОТОК 2
produceResults()
{locker.lock()
new
for(…);
tmp[]=myres;
изменения;
locker.unlock();
}
Слайд 354Например
ПОТОК 1
produceResults()
{locker.lock()
new
for(…);
tmp[]=myres;
изменения;
locker.unlock();
}
ПОТОК 2
produceResults()
{locker.lock()
new
for(…);
tmp[]=myres;
изменения;
locker.unlock();
}
Слайд 355Например
ПОТОК 1
produceResults()
{locker.lock()
new
for(…);
tmp[]=myres;
изменения;
locker.unlock();
}
ПОТОК 2
produceResults()
{locker.lock()
new
for(…);
tmp[]=myres;
изменения;
locker.unlock();
}
Слайд 356Например
ПОТОК 1
produceResults()
{locker.lock()
new
for(…);
tmp[]=myres;
изменения;
locker.unlock();
}
ПОТОК 2
produceResults()
{locker.lock()
new
for(…);
tmp[]=myres;
изменения;
locker.unlock();
}
Слайд 357Например
ПОТОК 1
produceResults()
{locker.lock()
new
for(…);
tmp[]=myres;
изменения;
locker.unlock();
}
ПОТОК 2
produceResults()
{locker.lock()
new
for(…);
tmp[]=myres;
изменения;
locker.unlock();
}
Слайд 358Например
ПОТОК 1
produceResults()
{locker.lock()
new
for(…);
tmp[]=myres;
изменения;
locker.unlock();
}
ПОТОК 2
produceResults()
{locker.lock()
new
for(…);
tmp[]=myres;
изменения;
locker.unlock();
}
Слайд 359QSemaphore
В отличие от мьютекса обладает несколькими ресурсами и переводится в заблокированное
состояние лишь когда будут зарезервированы все ресурсы. Объем ресурсов задается при создании.
Слайд 360QSemaphore
public:
QSemaphore(int n=0);
void acquire(int n=1);
void release(int n=1);
bool tryAcquire(int
n=1);
bool tryAcquire(int n=1, int timeout);
Слайд 361QSemaphore
ResType lst[100]; int resCount=0;
QSemaphore locker(100);
void produceResults()
{locker.acquire(n)
for( int i=0; i
)
lst[resCount++]=myres[i];
}
void consumeResults()
{// Извлекаем m результатов и сдвигаем
locker.release(m);
}
Слайд 362Например
Поток1
Поток2
ПотокN
Результаты
ПотокN+1
Слайд 363Например
Поток1
Поток2
ПотокN
Результаты
ПотокN+1
Слайд 364Например
Поток1
Поток2
ПотокN
Результаты
ПотокN+1
Слайд 365Например
Поток1
Поток2
ПотокN
Результаты
ПотокN+1
Слайд 366Например
Поток1
Поток2
ПотокN
Результаты
ПотокN+1
Слайд 367Например
Поток1
Поток2
ПотокN
Результаты
ПотокN+1
Слайд 368Например
Поток1
Поток2
ПотокN
Результаты
ПотокN+1
Слайд 369Например
Поток1
Поток2
ПотокN
Результаты
ПотокN+1
Слайд 370Например
Поток1
Поток2
ПотокN
Результаты
ПотокN+1