Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Infa (1)

.pdf
Скачиваний:
5
Добавлен:
14.04.2015
Размер:
2.31 Mб
Скачать

1) Компиляция и сборка программы. Динамические библиотеки.

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

Линковщик собирает рабочий файл изисходников скомпилированных компилятором. Если в исходниках отсутствуют соответствующие функции – он выдает ошибку.

вообще ошибки линковщика - это когда ты что-то объявил, но не создал. И пытаешься это использовать

Динамическая библиотека - библиотека, подключаемая к программе в момент выполнения. Это означает, что при создании библиотеки производится не только ее компиляция, но и линковка с другими, нужными ей, библиотеками (!).

Динамические библиотеки полезны в случаях, если:

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

-Важно использовать в программах на C библиотеки, подготовленные на C++ и при этом избежать лишних трудностей с линковкой программы;

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

Мы можем создать dll, что не является исполняемым файлом(не имеет точки входа в программу), но может выполнять некоторые функции, полезно, когда куча программ используют одни и те же функции.

Статическая линковка:

Вот содержание кода библиотеки:

#include<stdio.h>

_declspec(dllexport) int fact(int n);

int fact(intn)

{

int k = 1;

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

{

k *= i;

}

returnk;

}

Здесь функция вычисляет факториал числа.

_declspec(dllexport)говорит о том, что функция будет использоваться вне библиотеки, т.е. экспортируемая(dllexport) функция. При компиляции библиотеки создаются libи dllфайлы. Lib

– статическая библиотека. Dll – динамическая. Содержание файла программы:

#include<stdio.h> #pragmacomment(lib,"dllka.lib") __declspec(dllimport) int fact(int n);

Здесь мы линкуем статическую библиотеку с помощью #pragmacomment(lib,"dllka.lib"), что позволяет использовать функциифайла dllka.dll.

Далее создаем прототип функции, которая содержится в dllka.dll: __declspec(dllimport) int fact(int n);

Здесь __declspec(dllimport)говорит об импортируемой функции(dllimport)

Динамическая линковка:

Вот содержание кода библиотеки:

#include<stdio.h>

extern"C"__declspec(dllexport) int fact(int n);

int fact(intn)

{

int k = 1;

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

{

k *= i;

}

returnk;

}

extern"C"используется для приведения имени функции к человеческому виду в библиотеке, т.е. без него функция будет называться криво, к примеру “?fact@@YAHH@Z”, с ним – fact”

Надо заметить, что если при этом использовать статическую линковку, то нужно использовать:

extern"C"__declspec(dllimport) intfact(intn);

Продолжим с динамической линковкой библиотеки:

Для подключения функции будет использоваться указатель на функцию, для чего нужно создать такой тип:

typedefint (*func)(int);

#include<windows.h>

int main(intargc, char* argv[])

{

int a;

//a = fact(a);

HMODULE module = LoadLibrary(L"dllka.dll"); if (!module)

{

printf("Library not found"); return 1;

}

func fact = (func)GetProcAddress(module, "fact"); if (!fact)

{

printf("Function not found"); FreeLibrary(module);

return 2;

}

FreeLibrary(module);

}

Здесь подключение библиотеки идет при помощи LoadLibrary(L"dllka.dll");

L – говорит, что строка в формате Юникода, "dllka.dll"–название динамической библиотеки В случае, если библиотека не найдена функция LoadLibraryвернет 0. Тогдаможнопроверить:

if (!module)

{

printf("Library not found"); return 1;

}

Функция GetProcAddress(module, "fact"); берет, как видно переменную типа HMODULEи строку

– название функции, если функция не найдена вернет 0.

Замечание:

Если в исходном коде dllне было указано extern"C", то функция будет называться соответсвенно по-другому, к примеру “?fact@@YAHH@Z” и тогда будет выглядеть так: func fact = (func)GetProcAddress(module, "?fact@@YAHH@Z");

Еще нужно не забыть отключить библиотеку по окончании использования:

FreeLibrary(module);

2) Время жизни и область видимости программных объектов.

Время жизни переменной (глобальной или локальной) определяется по следующим правилам.

1.Переменная, объявленная глобально (т.е. вне всех блоков), существует на протяжении всего времени выполнения программы.

2.Локальные переменные (т.е. объявленные внутри блока) с классом памяти register или auto, имеют время жизни только на период выполнения того блока, в котором они объявлены. Если локальная переменная объявлена с классом памяти static или extern, то она имеет время жизни на период выполнения всей программы.

Видимость переменных и функций в программе определяется следующими правилами.

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

2.Переменная, объявленная или определенная локально, видима от точки объявления или определения до конца текущего блока. Такая переменная называется локальной.

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

4.Функции с классом памяти static видимы только в исходном файле, в котором они

определены. Всякие другие функции видимы во всей программе. Метки в функциях видимы на протяжении всей функции.

Имена формальных параметров, объявленные в списке параметров прототипа функции, видимы только от точки объявления параметра до конца объявления функции.

3) Перегрузка функций в C++. Параметры по умолчанию.

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

перегрузка функции,

передача значений параметрам в прототипе функции (аргументы по умолчанию).

Вкаких случаях следует использовать первый, а в каких второй?

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

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

Понятно, что перегрузку функций и установку аргументов по умолчанию можно использовать совместно.

Перегруженная функция (переменное число аргументов)

#include <iostream> using namespace std;

void repchar(); void repchar(char);

void repchar(char, int);

int main(){ repchar(); repchar('='); repchar('+',30);

return0;

}

void repchar(){ for(int j=0; j<45; j++)

cout <<'*'; cout << endl;

}

void repchar(char ch){ for(int j=0; j<45; j++)

cout << ch; cout << endl;

}

void repchar(char ch, int n){ for(int j=0; j<n; j++)

cout << ch;

cout << endl;

}

Вывод:

*********************************************

=============================================

++++++++++++++++++++++++++++++

Перегруженная функция (различные типы аргументов)

#include <iostream> using namespace std;

struct Distance { int feet;

float inches; };

void engldisp(Distance); void engldisp(float);

int main(){ Distance d1;

float d2;

cout <<"Введите число футов: "; cin >> d1.feet;

cout <<"Введите число дюймов: "; cin >> d1.inches;

cout <<"Введите длину в дюймах: "; cin >> d2;

cout <<"d1 = "; engldisp(d1); cout <<"\nd2 = "; engldisp(d2); cout << endl;

return0;

}

void engldisp(Distance dd){

cout << dd.feet<<"\'-"<< dd.inches<<"\"";

}

void engldisp(float dd){

int feet = static_cast<int>(dd/12); float inches = dd - feet *12;

cout << feet <<"\'-"<< inches <<"\"";

}

Введите число футов: 45

Введите число дюймов: 32

Введите длину в дюймах: 123

d1 = 45'-32"

d2 = 10'-3"

Установка аргументов по умолчанию

#include <iostream> using namespace std;

void repchar(char='*', int=45);

int main(){ repchar(); repchar('=');

repchar('+',30); return0;

}

void repchar(char ch, int n){ for(int j=0; j<n; j++)

cout << ch; cout << endl;

}

Вывод:

*********************************************

=============================================

++++++++++++++++++++++++++++++

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

Перегрузка функций выглядит так: intmax(int a, int b)

{

if(a>b)

return 1;

else

return 0;

}

intmax(double a, double b)

{

if(a>b)

return 1;

else

return 0;

}

Они могут возвращать только одно значение, а принимать разные, а также разное количество. Плюсы в том, что можно определить конкретно нужные функции, но в минусах, если при таком раскладе вызвать

float a, b; max(a,b);

Линковщик ругнется, сказав, что нет соответствующей функции(в отличии от того, если была одна функция intmax(double a, double b){ })

Параметры по умолчанию:

Для совместимости используют параметры по умолчанию, т.е. они могут не указываться при вызове и будут иметь какое-то значение(b=0):

intmax(double a, double b = 0);

Вызов без указания параметра: l = max(c);

Вызов с указанием: l = max(t,n);

у функции могут быть параметры по

умолчанию

они задаются после всех остальных

int func(int a, int b, int c = 0);

как-то так

при вызове такой функции указывать параметры по умолчанию не обязательно

т.е. в данном случае:

l = func(k,p);

и

l = func(k,p,t);

будут работать

если их не указываешь, то им присваивается значение по умолчанию

в данном случае переменной с присваивается значение 0

когда вызываем l = func(k,p,t);

тогда a = k, b = p, c = t

т.е. это такие переменные, которые не обязательно чему-то равны

их не обязательно указывать, точнее

4) Динамическое выделение памяти.

Само выделение памяти:

int *p = (int *)malloc(1000000 * sizeof(int)); /* т.е. в языке С++ обязательно привести указатель void* к типу int* (объявление функции malloc выглядит так: void* malloc(size_t size); - поэтому в С++ его нужно привести к типу int*) */

3 области памяти

 

 

Глобальная память

Стек

Куча

Размер определяется в момент

Размер определяется в

Размер увеличивается в процессе

компиляции

момент запуска

работы программы

Область памяти "куча" есть у каждой программы. Ее размером управляет операционная система. Запросы на изменение ее размера обрабатывает тоже операционная система.

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

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

Функция malloc обращается к операционной среде с просьбой выделить место в куче и, если ОС выделяет это место, возвращает укзатель на начало области. Если выделить память не удалось, malloc возвращает 0.

if (p == 0){

/* памяти не хватило */

};

После использования массива нужно освободить место в куче, используя функцию free(p) - объявляет эту область памяти свободной и передает под управление ОС. После этого

операция p[10] вызовет ошибку, т.к. эта область памяти уже не принадлежит программе.

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

Аналогично, free тоже не быстрая функция. Следовательно, весь процесс выделенияосвобождения памяти занимает обычно длительное время и нужно его оптимизировать в сторону уменьшения количества использований функций malloc и free.

Способ оптимизации: завести массив указателей на int* длины N и массив

из int размера N*N. После этого записать в M[i] указатель на i-й участок второго массива. Итого malloc вызван всего 2 раза вместо N+1 раза.

Вязыке С есть еще несколько функций, связанных с работой с памятью:

calloc - выделяет память и инициализирует ее нулями

realloc - изменяет размер уже существующего массива. Существует три результата работы функции:

o если нужное число байт не занято в смежной области, то увеличивает область для массива

o если рядом нет свободной памяти, перенесет массив в другое место o если вообще нет памяти под увеличенный массив, вернет 0

Вязыке С++ есть альтернатива malloc и free:

int *p = new int[size]; /* вернет 0, если не хватает памяти */ delete[] p; /* освобождает память */

В этом случае нам не нужно писать, сколько байт выделить для массива, достаточно количества элементов, ведь компилятор уже знает, какого типа они будут.

Важно:

Нельзя использовать вместе new и free или malloc и delete!

5) Классы в C++.

Одной из основных черт C++, которой нет в С, является концепция классов. По существу, классы - самое важное понятие в C++. Классы похожи на структуры языка С. Однако структура С определяет только данные, ассоциированные с этой структурой.

Вот пример структуры С:

struct CIRCLE

{

int radius;

int color;

{;

После того как вы объявили структуру, вы можете использовать ее в пределах вашей функции main (), как показано ниже:

void main() {

CIRCLE MyCircle;

...

...

MyCircle.radius = 18;

MyCircle.color = 255; // 255 задает цвет

...

...

}

Со структурой MyCircle (представляющей окружность) ассоциируются данные radius и color (радиус и цвет). Класс в C++, с другой стороны, имеет как ассоциированные с ним данные, так и функции. Данные класса называются элементами данных, а функции класса - элементамифункциями. Следовательно, в программе, которая использует классы, можно написать следующий код:

MyCircle.radius = 20;

MyCircle.color = 255;

MyCircle.DisplayCircle() ;

Первые два оператора присваивают значения элементам данных MyCircle radius и color; третий оператор вызывает функцию-элемент DisplayCircle() для вывода окружности MyCircle. MyCircle называется объектом класса circle. Ваша программа может объявить другой объект с именем HerCircle класса circle следующим образом:

CIRCLE HerCircle;

Следующие операторы присваивают значения элементам данных HerCircle radius и color:

HerCircle.radius = 30;

HerCircle.color = 0;

Затем вы можете использовать функцию-элемент DisplayCircie () для вывода окружности

HerCircle:

HerCircle.DisplayCircle();

Объявление класса

Перед тем как работать с классом, ваша программа должна его объявить (так же как перед работой со структурой mystructure вы должны были объявить ее элементы данных). В данном разделе вы познакомитесь с синтаксисом объявления класса. Вы будете и дальше практиковаться с классом circle:

class Circle ( public: Circle () ;

void SetRadius(void) ; void GetRadius(void) ;

~Circle () ;

private:

void CalculateArea(void); int radius;

int color;

};

Объявление класса имеет следующее строение:

class Circle {

...

...

Здесь вы вводите объявление класса

...

...

};

Ключевое слово class показывает компилятору, что все находящееся в фигурных скобках ({}) принадлежит объявлению класса. (Не забывайте ставить точку с запятой в конце объявления.) Объявление класса содержит объявление элементов данных (например, int radius) и прототипы функций-элементов класса. В объявлении класса circle содержатся следующие элементы данных:

int radius;

int color;

Объявление также содержит пять прототипов функций-элементов:

Circle();

void SetRadius(void) ; void GetRadius(void) ; ~Circle () ;

void CalculateArea(void);

Первый и четвертый прототипы выглядят странно. Первый из них является прототипом функции конструктора:

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]