Динамическое и статистическое связывание в языке C++
Связывание в языке C++. Решение о выборе метода статического или динамического типа. Описание классов и глобальных переменных. Метод для обращения полиморфизма. Разработка библиотеки функций. Разработка приложения на языке С++ с применением Win32 API.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | контрольная работа |
Язык | русский |
Дата добавления | 08.05.2012 |
Размер файла | 540,0 K |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
Размещено на http://www.allbest.ru/
Динамическое и статистическое связывание в языке C++
Введение
Системное программирование (или программирование систем) - подраздел программирования, заключающийся в работе над системным программным обеспечением.
Определение «системное» подчеркивает тот факт, что результаты этого вида программирования существенно меняют свойства и возможности вычислительной системы. В то же время бесспорным остаётся тот факт, что в определенной степени этот результат имеет место при применении любых программ, выполняемых в вычислительной системе. Поэтому между программированием «системным» и «несистемным» (прикладным программированием») нет четкой границы.
Вычислительная система имеет иерархическую структуру, которую можно представить в виде набора вложенных слоев, на внешнем из которых находятся прикладные программы, а на самом внутреннем - аппаратура компьютера. Условная степень системности нарастает при программировании, затрагивающем все более внутренние уровни системы.
Одной из основных отличительных черт системного программирования по сравнению с прикладным заключается в том, что результатом последнего является выпуск программ для взаимодействия с пользователем (например, текстовый процессор). В то время как результатом системного программирования является выпуск программ для взаимодействия с аппаратным обеспечением (например, дефрагментация жёсткого диска), что подразумевает сильную зависимость таких программ от аппаратной части. В частности выделим следующее:
программист должен учитывать специфику аппаратной части и другие, часто уникальные, свойства системы в которой функционирует программа, использовать эти свойства, например, применяя специально оптимизированный для данной архитектуры алгоритм;
часто используется низкоуровневый язык программирования или такой диалект языка программирования, который позволяет функционирование в окружении с ограниченным набором системных ресурсов,
работает максимально эффективно и имеет минимальное запаздывание по времени завершения,
имеет маленькую библиотеку времени выполнения (RTL) или не имеет её вообще,
позволяет прямое управление (прямой доступ) к памяти и управляющей логике,
позволяет делать ассемблерные вставки в код;
отладка программы может быть затруднена при невозможности запустить её в отладчике из-за ограничений на ресурсы, поэтому может применяться компьютерное моделирование для решения этой проблемы.
Подводя итог, можно утверждать, что системным называть подраздел программирования как вида инженерной деятельности, в котором программист использует специфические и часто уникальные свойства и возможности внутренних уровней вычислительной системы.
1. Связывание в языке C++
Двумя основными целями при разработке языка программирования С++ были эффективное использование памяти и скорость выполнения. Он был задуман как усовершенствование языка С, в частности, для объектно-ориентированных приложений. Основной принцип С++: никакое свойство языка не должно приводить к возникновению дополнительных издержек (как по памяти, так и по скорости), если данное свойство программистом не используется. Например, если вся объектная ориентированность С++ игнорируется, то оставшаяся часть должна работать так же быстро, как и традиционный С. Поэтому неудивительно что большинство методов в С++ связываются статически (во время компиляции), а не динамически (во время выполнения).
Связывание методов в этом языке является довольно сложным. Для обычных переменных (не указателей или ссылок) оно осуществляется статически. Но когда объекты обозначаются с помощью указателей или ссылок, используется динамическое связывание. В последнем случае решение о выборе метода статического или динамического типа диктуется тем, описан ли соответствующий метод с помощью ключевого слова virtual. Если он объявлен именно так, то метод поиска сообщения базируется на динамическом классе, если нет на статическом. Даже в тех случаях, когда используется динамическое связывание, законность любого запроса определяется компилятором на основе статического класса получателя.
Рассмотрим, например, следующее описание классов и глобальных переменных: class Mammal
{
public:
void speak()
{
printf («cant speak»);
}
};
class Dog: public Mammal
{
public:
void speak()
{
printf («wouf wouf»);
}
void bark()
{
printf («wouf wouf, as well»);
}
};
Mammal fred;
Dog lassie;
Mammal *fido = new Dog;
Выражение fred.speak() печатает «cant speak», однако вызов fido->speak() также напечатает «cant speak», поскольку соответствующий метод в классе Mammal не объявлен как виртуальный. Выражение fido->bark() не допускается компилятором, даже если динамический тип для fido класс Dog. Тем не менее статический тип переменной всего лишь класс Mammal.
Если мы добавим слово virtual:
class Mammal
{
public:
virtual void speak()
{
printf («cant speak»);
}
};
то получим на выходе для выражения fido->speak() ожидаемый результат.
Относительно недавнее изменение в языке С++ добавление средств для распознавания динамического класса объекта. Они образуют систему RTTI (Run-Time Type Identification идентификация типа во время выполнения).
В системе RTTI каждый класс имеет связанную с ним структуру типа typeinfo, которая кодирует различную информацию о классе. Поле данных name одно из полей данных этой структуры содержит имя класса в виде текстовой строки. Функция typeid может использоваться для анализа информации о типе данных. Следовательно, следующая ниже команда будет печатать строку «Dog» динамический тип данных для fido. В этом примере необходимо разыменовывать переменную-указатель fido, чтобы аргумент был значением, на которое ссылается указатель, а не самим указателем:
cout << «fido is a» << typeid(*fido).name() << endl;
Можно также спросить, используя функцию-член before, соответствует ли одна структура с информацией о типе данных подклассу класса, соотносящегося с другой структурой. Например, следующие два оператора выдают true и false:
if (typeid(*fido).before (typeid(fred)))…
if (typeid(fred).before (typeid(lassie)))…
До появления системы RTTI стандартный программистский трюк состоял в том, чтобы явным образом закодировать в иерархии класса методы быть экземпляром. Например, для проверки значения переменных типа Animal на принадлежность к типу Cat или к типу Dog можно было бы определить следующую систему методов:
class Mammal
{
public:
virtual int isaDog()
{
return=0;
}
virtual int isaCat()
{
return=0;
}
};
class Dog: public Mammal
{
public:
virtual int isaDog()
{
return=1;
}
};
class Cat: public Mammal
{
public:
virtual int isaCat()
{
return=1;
}
};
Mammal *fido;
Теперь для определения того, является ли текущим значением переменной fido величина типа Dog, можно использовать команду fido->isaDog(). Если возвращается ненулевое значение, то можно привести тип переменной к нужному типу данных.
Возвращая указатель, а не целое число, мы объединяем проверку на принадлежность к подклассу и приведение типа. Это аналогично другой части системы RTTI, называемой dynamic_cast, которую мы вкратце опишем. Если некая функция в классе Mammal возвращает указатель на Dog, то класс Dog должен быть предварительно описан. Результатом присваивания является либо нулевой указатель, либо правильная ссылка на класс Dog. Итак, проверка результата все еще должна осуществляться, но мы исключаем необходимость приведения типа. Это показано в следующем примере:
class Dog; // предварительное описание
class Cat;
class Mammal
{
public:
virtual Dog* isaDog()
{
return=0;
}
virtual Cat* isaCat()
{
return=0;
}
};
class Dog: public Mammal
{
public:
virtual Dog* isaDog()
{
return this;
}
};
class Cat: public Mammal
{
public:
virtual Cat* isaCat()
{
return this;
}
};
Mammal *fido;
Dog *lassie;
Оператор lassie = fido->isaDog(); теперь выполним всегда. В результате переменная lassie получает ненулевое значение, только если fido имеет динамический класс Dog. Если fido не принадлежит Dog, то переменной lassie будет присвоен нулевой указатель.
lassie = fido->isaDog();
if(lassie)
{
… // fido и в самом деле относится к типу Dog}
else
{… // присваивание не сработало
… // fido не принадлежит к типу Dog};
Хотя программист и может использовать этот метод для обращения полиморфизма, недостаток такого способа состоит в том, что требуется добавление методов как в родительский, так и в дочерний классы. Если из одного общего родительского класса проистекает много дочерних, метод становится громоздким. Если изменения в родительском классе не допускаются, такая техника вообще невозможна.
Поскольку подобные проблемы встречаются часто, было найдено их общее решение. Функция шаблона dynamic_cast берет тип в качестве аргумента шаблона и, в точности как функция, определенная выше, возвращает либо значение аргумента (если приведение типа законно), либо нулевое значение (если приведение типа неразрешено). Присваивание, эквивалентное сделанному в предыдущем примере, может быть записано так:
// конвертировать только в том случае, если fido является собакой
lassie = dynamic_cast < Dog* > (fido);
// затем проверить, выполнено ли приведение
if (lassie)…
В язык C++ были добавлены еще три типа приведения (static_cast, const_cast и reinterpret_cast), но они используются в особых случаях и поэтому здесь не описываются. Программистам рекомендуется применять их как более безопасные средства вместо прежнего механизма приведения типов.
2. Проектная часть
2.1 Разработка библиотеки функций
2.1.1 Постановка задачи
Разработать библиотеку функций, назначение которых описано в таблице 2.1, и использовать эти функции в основной программе.
Таблица 2.1 - Назначение функций
Номер варианта |
Подпрограмма №1 |
Подпрограмма №2 |
|
3 |
Функция, возвращающая значение среднего арифметического элементов целочисленного массива. |
Процедура перестановки элементов целочисленного массива в следующем порядке: 1-ый элемент меняется местами с последним, 2-ой - с предпоследним и т.д. |
2.1.2 Программный код
program Zad_1; {заголовок программы}
uses {подключение дополнительных модулей}
crt;
type {описание типов}
massiv = array [1..50] of integer; {тип - целочисленный массив с максимальным числом элементов 50}
{функция расчета среднего арифметического элементов массива
Параметры:
mas - массив
n - количество элементов в массиве
Возвращаемое значение:
среднее арифметическое элементов массива, тип - real}
function SredArif (mas:massiv; n:byte): real;
var
i: byte;
sum: integer;
begin
sum:= 0;
for i:=1 to n do
sum:= sum + mas[i];
SredArif:= sum /n;
end;
{процедура перестановки элементов массива местами,
Параметры:
mas - массив
n - количество элементов в массиве
Результат:
выводит на экран элементы матрицы, которые были поменяны местами,
т.е. 1-ый элемент становиться последним, 2-ой предпоследним и т.д.}
procedure Perestanovka (a:massiv; n:byte);
var
i: byte;
tmp: integer;
begin
for i:=1 to (n div 2) do
begin
tmp:= a[i];
a[i]:=a [n-i+1];
a [n-i+1]:= tmp;
end;
for i:=1 to n do
Write (a[i], ' ');
end;
var
mas: massiv;
i, n: byte;
Begin
clrscr;
Write ('Введите количество элементов в массиве:');
ReadLn(n);
for i:=1 to n do
begin
Write ('Введите ', i, '-ый элемент массива:');
ReadLn (mas[i]);
end;
WriteLn ('1. Среднее арифметическое =', sredArif (mas, n):0:2);
ReadLn;
WriteLn ('2. Перестановка элементов массива местами: ');
ReadLn;
End.
2.1.3 Пример работы программы
Рисунок 2.1 - Пример 1
Рисунок 2.2 - Пример 2
2.2 Разработка приложения на языке С++ с применением Win32 API
язык переменная полиморфизм приложение
2.2.1 Постановка задачи
Необходимо формировать и вывести на экран временную диаграмму напряжения переменного электрического тока. Действующее значение напряжения - 220 В, частота - 50 Гц. Диаграмма строится для промежутка времени, в котором происходят ровно два периода колебаний.
2.2.2 Программный код
//KWnd.h
#include «KWnd.h»
KWnd:KWnd (LPCTSTR windowName, HINSTANCE hInst, int cmdShow,
LRESULT (WINAPI *pWndProc) (HWND, UINT, WPARAM, LPARAM),
LPCTSTR menuName, int x, int y, int width, int height,
UINT classStyle, DWORD windowStyle, HWND hParent)
{
char szClassName[] = «KWndClass»;
wc.cbSize = sizeof(wc);
wc.style = classStyle;
wc.lpfnWndProc = pWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wc.lpszMenuName = menuName;
wc.lpszClassName = szClassName;
wc.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
// ? aaeno? e? oai eeann ieia
if (! RegisterClassEx(&wc)) {
char msg[100] = «Cannot register class:»;
strcat (msg, szClassName);
MessageBox (NULL, msg, «Error», MB_OK);
return;
}
// Nicaaai ieii
hWnd = CreateWindow (szClassName, windowName, windowStyle,
x, y, width, height, hParent, (HMENU) NULL, hInst, NULL);
if (! hWnd) {
char text[100] = «Cannot create window:»;
strcat (text, windowName);
MessageBox (NULL, text, «Error», MB_OK);
return;
}
// Iieacuaaai ieii
ShowWindow (hWnd, cmdShow);
}
/////////////////////////////////////////
//U_T_Diagram.cpp
#define _WIN32_WINNT 0x500
#include <windows.h>
#include <math.h>
#include «KWnd.h»
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
void DrawDiagram (HWND, HDC);
#define Pi 3.14159265
int WINAPI WinMain (HINSTANCE hlnstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
KWnd mainWnd («Временная диаграмма напряжения переменного электрического тока», hlnstance, nCmdShow, WndProc);
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
PAINTSTRUCT ps;
switch (uMsg)
{
case WM_PAINT:
hDC = BeginPaint (hWnd, &ps);
DrawDiagram (hWnd, hDC);
EndPaint (hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc (hWnd, uMsg, wParam, lParam);
}
return 0;
}
void DrawDiagram (HWND hwnd, HDC hdc)
{
RECT rect;
GetClientRect (hwnd, &rect);
const int xVE = rect.right - rect.left;
const int yVE = rect.bottom - rect.top;
const int xWE = xVE;
const int yWE = yVE;
int nPixPerVolt = 1;
int nPixPerMs = 10;
SetMapMode (hdc, MM_ISOTROPIC);
SetWindowExtEx (hdc, xWE, yWE, NULL);
SetViewportExtEx (hdc, xVE, - yVE, NULL);
SetViewportOrgEx (hdc, 10*nPixPerMs, yVE/2, NULL);
const int tMin = 0; // ms
const int tMax = 40; // ms
const int uMin = -400; // V
const int uMax = 400; // V
const int tGridStep = 5;
const int uGridStep = 100;
int x, y;
char* xMark[] = {«0», «5», «10», «15», «20», «25», «30», «35», «40»};
char* yMark[] = {«- 400»,» - 300»,» - 200»,» - 100», «0», «100», «200», «300», «400»};
// Сетка
HPEN hPenO = CreatePen (PS_SOLID, 1, RGB (0,160, 0));
HPEN hOldPen = (HPEN) SelectObject (hdc, hPenO);
int u = uMin;
int xMin = tMin * nPixPerMs;
int xMax = tMax * nPixPerMs;
for (int i = 0; i < 9; ++i)
{
y = u * nPixPerVolt;
MoveToEx (hdc, xMin, y, NULL);
LineTo (hdc, xMax, y);
TextOut (hdc, xMin-40, y+8, yMark[i], strlen (yMark[i]));
u += uGridStep;
}
int t = tMin;
int yMin = uMin * nPixPerVolt;
int yMax = uMax * nPixPerVolt;
for (i =0; i < 9; ++i) {
x = t * nPixPerMs;
MoveToEx (hdc, x, yMin, NULL);
LineTo (hdc, x, yMax);
TextOut (hdc, x-6, yMin-10, xMark[i], strlen (xMark[i]));
t += tGridStep;
}
// Ось «х»
HPEN hPenl = CreatePen (PS_SOLID, 3, RGB (0, 0, 0));
SelectObject (hdc, hPenl);
MoveToEx (hdc, 0, 0, NULL);
LineTo (hdc, xMax, 0);
static LOGFONT lf;
lf.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
lf.lfItalic = TRUE;
lf.lfWeight = FW_BOLD;
lf.lfHeight = 20;
lf.lfCharSet = DEFAULT_CHARSET;
lstrcpy((LPSTR)&lf.lfFaceName, «Arial»);
HFONT hFontO = CreateFontIndirect(&lf);
HFONT hOldFont = (HFONT) SelectObject (hdc, hFontO);
SetTextColor (hdc, RGB (0, 0, 200));
TextOut (hdc, xMax+10, 10, «t (ms)», 6);
// Ось «у»
MoveToEx (hdc, 0, yMin, NULL);
LineTo (hdc, 0, yMax);
TextOut (hdc, -10, yMax+30, «u (V)», 5);
// График
HPEN hPen2 = CreatePen (PS_SOLID, 5, RGB (200, 0, 100));
SelectObject (hdc, hPen2);
int tStep = 1;
double radianPerMs = 2 * Pi /20;
const double uAmplit = 311.17; // volt
t = tMin;
MoveToEx (hdc, 0, 0, NULL);
while (t <= tMax) {
u = (int) (uAmplit * sin (t * radianPerMs));
LineTo (hdc, t * nPixPerMs, u * nPixPerVolt);
t += tStep;
}
// Заголовок
char* title = «Диаграмма напряжения переменного электр. тока»;
lf.lfItalic = FALSE;
lf.lfWeight = FW_BOLD;
lf.lfHeight = 30;
HFONT hFontl = CreateFontIndirect(&lf);
SelectObject (hdc, hFontl);
SetTextColor (hdc, RGB (0, 200, 0));
TextOut (hdc, 0, yMax + 70, title, strlen(title));
SelectObject (hdc, hOldPen);
SelectObject (hdc, hOldFont);
}
//KWnd.h
#include <windows.h>
class KWnd {
public:
KWnd (LPCTSTR windowName, HINSTANCE hInst, int cmdShow,
LRESULT (WINAPI *pWndProc) (HWND, UINT, WPARAM, LPARAM),
LPCTSTR menuName = NULL,
int x = CW_USEDEFAULT, int y = 0,
int width = CW_USEDEFAULT, int height = 0,
UINT classStyle = CS_HREDRAW | CS_VREDRAW,
DWORD windowStyle = WS_OVERLAPPEDWINDOW,
HWND hParent = NULL);
HWND GetHWnd() {return hWnd;}
protected:
HWND hWnd;
WNDCLASSEX wc;
};
2.2.3 Пример работы программы
Рисунок 2.3 - Диаграмма напряжения переменного электрического тока
Заключение
Си++ как преемник языка Си широко используется в системном программировании. На нем можно писать высокоэффективные программы, в том числе операционные системы, драйверы и т.п. Язык Си++ - один из основных языков разработки трансляторов.
Поскольку системное программное обеспечение часто бывает написано на языке Си или Си++, то и программные интерфейсы к подсистемам ОС тоже часто пишут на Си++.
API - это аббревиатура названия Application Programming Interface (интерфейс прикладного программирования). API представляет собой совокупность функций и инструментов, позволяющих программисту создавать приложения (программы), работающие в некоторой среде.
Win32 API - это набор функций для создания программ, работающих под управлением Microsoft Windows 98, Windows NT или Windows 2000. Все функции этого набора являются 32-битными, что отражено в названии интерфейса.
Windows API был изначально спроектирован для использования в программах, написанных на языке C (или C++). Работа через Windows API - это наиболее близкий к системе способ взаимодействия с ней из прикладных программ. Более низкий уровень доступа, необходимый только для драйверов устройств, в текущих версиях Windows предоставляется через Windows Driver Model.
В процессе выполнения курсовой работы были приобретены навыки работы с языком C++. В аналитической части был разобран материал по динамическому и статическому связыванию методов.
В практической части изучен принцип работы с массивами и создания библиотек, а также навыкам создания приложения на языке С++ с использование Win32 API.
Список литературы
1 Е.А. Зуев. Программирование на языке Turbo Pascal 6.0, 7.0, М.: Веста, Радио и связь, 1993.
2 Моргун Александр Николаевич. Справочник по Turbo Pascal для студентов. - М.: Диалектика, 2006.
3 Щупак Ю.А. Win32 API. Эффективная разработка приложений. - СПб.: Питер, 2007.
Размещено на Allbest.ru
Подобные документы
Выбор метода проектирования транслятора с языка Паскаль на язык Си, разработка и кодирование алгоритма программы. Использование допустимых операторов в исходном тексте, определение типов переменных и синтаксиса логических и арифметических выражений.
курсовая работа [1,0 M], добавлен 03.07.2011Проектирование арифметико-логических устройств (АЛУ). Отладка описания логических схем на языке VHDL. Классификация АЛУ по способу представления чисел, характеру использования элементов и узлов. Список стандартных функций АЛУ, его описание на языке VHDL.
лабораторная работа [633,4 K], добавлен 11.03.2014Разработка программы "Игроки КХЛ 2012-2013" на языке С++ с использованием классов списков структур для обработки данных. Описание глобальных переменных, разработанных функций. Главное меню программы. Чтение данных из файла, их просмотр и сохранение.
курсовая работа [2,2 M], добавлен 17.03.2016Разработка оконного приложения на языке C#, в окне которого будет игра "Лабиринт. Диаграмма вариантов использования языка UML. Описание разрабатываемой программы с точки зрения программиста. Разработка прикладного окна, классов Enemy и Dot, Wall и Map.
курсовая работа [457,6 K], добавлен 22.06.2015Разработка трехмерной модели приложения "Гоночный автомобиль" на языке С++ с использованием библиотеки OpenGL и MFC, создание программы в среде Visual Studio 6.0. Информационное обеспечение, логическая структура и функциональная декомпозиция проекта.
курсовая работа [3,9 M], добавлен 29.06.2011Структура модели на языке Express. Правила записи супертипов и подтипов. Разработка информационных моделей в рамках концепции CALS. Типы данных в языке Express. Структура портативного зарядного устройства ЗарядON. Изображение сущности на языке Express-G.
курсовая работа [487,9 K], добавлен 18.01.2013Разработка программ средствами библиотеки tkinter на языке Python. Изучение основы работы в текстовом редакторе Word. Описание авторской идеи анимации. Использование базовых команд и конструкций. Процесс проектирования и алгоритм разработанной программы.
контрольная работа [125,3 K], добавлен 11.11.2014Компиляция программ на языке C/C++. Компиляция нескольких файлов. Библиотеки объектных файлов. Создание статической и динамической библиотеки. Функции работы. Создание динамической библиотеки для решения системы линейных уравнений.
курсовая работа [27,4 K], добавлен 07.08.2007Разработка программы для рисования различных правильных многоугольников с помощью объектно-ориентированного языка программирования. Использование для разработки среды C++ Builder 6 и библиотеки VCL. Разработка интерфейса приложения и алгоритма его работы.
курсовая работа [616,4 K], добавлен 18.10.2010Создание программы для перевода кодов с языка Pascal на язык Си. Обработка программ операторами case, assign, rewrite и write. Способы объявления файла, комментария, переменных, логических и арифметических выражений. Виды синтаксических анализаторов.
курсовая работа [461,0 K], добавлен 03.07.2011