вторник, 5 июня 2012 г.

Инкапсулация

Инкапсулация в широком смысле упаковка данных вместе с функциями.

(с) Б. Эккель

воскресенье, 3 июня 2012 г.

Условная компиляция

Часто необходимо некоторым способом отладить программу. Конечно это надо делать с помощью отладчика типа gdb. Но иногда случается так, что отладчика нет под рукой и тогда на помощь может прийти условная компиляция с выводом отладочной информации. Но отсутствие отладчика не такая уж часто встречаемая проблема. Есть и другая задача, где применение условной компиляции неизбежно. Представьте себе, что ваша программа будет работать на нескольких платформах и в зависимости от платформы необходимо выполнять те или иные действия. Например подключать специфические для  данной ОС заголовочные файлы и соответственно вызывать разные функции. Здесь уж точно не обойтись без условной компиляции.
Для условной компиляции используются директивы препроцессора. Как известно директивы препроцессора начинаются с символа #. Например:

 #include <iostream>  //деректива препроцессора которая подключает заголовочный файл

Для условной компиляции используется директива

#ifdef NAME
...
#endif
или
#ifdef NAME
...
#elseif
...
#endif

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

#include <iostream>

int main()
{
        int var = 5;

        #ifdef DEBUG
                std::cout << "var: " << var << "\n";
        #endif
}

Для вывода отладочной информации необходимо определить имя DBUG в командной строке компилятора. Для компилятора GCC данная команда выполняется с помощью ключа -D name, где name - имя определяемой константы. Например:

g++ test.cpp -o test -D DEBUG

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

суббота, 2 июня 2012 г.

Представление float в памяти

Размер float на моем компьютере равен 4 байтам или 32 битам:
cout << sizeof(float); // 4
Согласно стандарту IEEE 754 формат представления float в памяти компьютера следующий:

0
0
1
1
1
1
1
1
1
0
0
1
1
1
1
0
0
0
0
0
0
1
0
0
0
0
0
1
1
0
0
1
1 8 23
Это представление числа 1,2345.

Т.е. 1 бит отводится под знак, 8 бит отводится для представления целой части и оставшиеся 23 бита для представления дробной части. А само вычисление производится по формуле:

(-1)^{sing}(1+\displaystyle\sum_{i=1}^{23} b_{-i}2^{-i})2^{e-127}

Пример: Возьмем число изображенное выше 1,2345. Согласно формуле (-1)^0=1, т.е. наше число положительно. Далее вычисляем показатель степени. Для этого переведем двоичное значение 8ми битного поля целой части в десятичное представление:

01111111 =  1*2^0+1*2^1+1*2^2+1*2^3+1*2^4+1*2^5+1*2^6+0*2^7=
=1+2+4+8+16+32+64=127

Вычисляем по формуле:

2^(127-127)=2^0=1

Осталось вычислить дробную часть:

00111100000010000011001=1*2^(-3)+1*2^(-4)+1*2^(-5)+1*2^(-6)+1*2^(-13)+1*2^(-19)+1*2^(-20)+1*2^(-23)=0,125+0,0625+0,03125+0,01563+0,00012+0,0000019073+0,0000009537+0.000000119=(приблизительно)0,2345

Окончательно имеем 1+0,2345=1,2345.
Для того что бы увидеть битовое представление float на компьютере можно воспользоваться маленькой программкой:

#include <iostream>
using namespace std;

// Печать одной байта в битах
void printInBinary(const unsigned char val)
{
        for (int i = 7; i &gt;= 0; --i)
                if (val &amp; (1 &lt;&lt; i))
                        cout &lt;&lt; "1";
                else
                        cout &lt;&lt; "0";
}

int main()
{
        float f = 1.2345;
        unsigned char *ucp = 
              reinterpret_cast<unsigned char*="">(&amp;f);

        for (int i = sizeof(float) - 1; i &gt;= 0; --i)
                printInBinary(ucp[i]);
}

понедельник, 28 мая 2012 г.

Трансляторы

Традиционно трансляторы делятся: интерпретаторы и компиляторы.

Интерпретатор - транслирует исходный текст программы в операции (которые могут состоять из нескольких машинных команд) и немедленно выполняет эти операции.

Компилятор - транслирует исходный текст программы в машинные команды, которые записывает в файл.

По мотивам Эккеля

вторник, 22 мая 2012 г.

Механизмы ООП

На одном из собеседований при трудоустройстве на работу меня спросили определения механизмов ООП. Своими словами и на примерах я постарался объяснить, но формальных определений так и не смог дать.В качестве памятки составлю пост.


Объектно-ориентированное, или объектное, программирование (в дальнейшем ООП) — парадигма программирования, в которой основными концепциями являются понятия объектов и классов.[0]


Механизмы ООП:
  • Инкапсуляция — свойство языка программирования, позволяющее пользователю не задумываться о сложности реализации используемого программного компонента (то, что у него внутри), а взаимодействовать с ним посредством предоставляемого интерфейса (публичных членов — методов, данных etc.), а также объединить и защитить жизненно важные для компонента данные.[1]
  • Полиморфизм — возможность объектов с одинаковой спецификацией иметь различную реализацию. Полиморфизмом в программировании называется переопределение наследником функций-членов базового класса.[2]
  • Наследование — механизм позволяющий описать новый класс на основе уже существующего (родительского), при этом свойства и функциональность родительского класса заимствуются новым классом. В C++ при наследовании одного класса от другого наследуется реализация класса, плюс класс-наследник может добавлять свои поля и функции или переопределять функции базового класса.[3]
Википедия пишет про еще один механизм:
  • Абстракция — это придание объекту характеристик, которые четко определяют его концептуальные границы, отличая от всех других объектов. Основная идея состоит в том, чтобы отделить способ использования составных объектов данных от деталей их реализации в виде более простых объектов, подобно тому, как функциональная абстракция разделяет способ использования функции и деталей её реализации в терминах более примитивных функций, таким образом, данные обрабатываются функцией высокого уровня с помощью вызова функций низкого уровня.[4]

воскресенье, 20 мая 2012 г.

Процесс разработки ПО

Имеет 3 этапа:
  1. Анализ: определение границ решаемой проблемы;
  2. Проектирование: создание общей структуры системы;
  3. Реализация: написание и тестирование программы;
(с) Б. Страуструп

воскресенье, 17 июля 2011 г.

настройка Vim

Для перехода на новую строку с отступом предыдущей строки

set smartindent

в файле vimrc

суббота, 16 июля 2011 г.

Установка QNX Neutrino (Шаг 2)

Для написание программ которые будут запускаться на QNX Neutrino Realtime Operating System (RTOS) во-первых вам потребуется QNX Software Development Platform (SDP), которая была установлена в предыдущей части. Так же вам понадобится установленная QNX Neutrino для запуска написанных вами программ. Существует несколько вариантов установки операционной системы QNX Neutrino. Все они детально описаны в официальном руководстве по установке здесь.

вторник, 12 июля 2011 г.

QNX Momentics IDE (Шаг 1)

             QNX Momentics IDE это интегрированная среда разработки для построения встроенных систем на базе ОС QNX Neutrino. Она является частью пакета разработки QNX Momentics development suite и предоставляется бесплатно для не коммерческого использования на сайте qnx.com. QNX Momentics IDE построена на базе IDE Eclipse и может быть запущена на различных платформах где может быть установлена виртуальная машина Java: Windows, Linux, Solaris и собственно на ОС QNX Neutrino
            После загрузки и установки дистрибутива IDE соответствующей вашей ОС на рабочем столе появится иконка. При первом запуске IDE в среде Windows вам предложат выбрать рабочую папку (workspace), где будут сохраняться все проекты. По умолчанию предлагается home_directory/ide4-workspace в Neutrino, Linux и Solaris, и C:/QNX/632/ ide4-workspace в Windows. Затем запуститься среда и отобразится окно приветствия. Для того что бы создать проект и приступить к написанию своего первого embedded-приложения нажмите на иконку Workbench а затем в меню File->New->C++ Project:

воскресенье, 3 июля 2011 г.

Комплексные числа

Мой класс описывающий базовые операции с комплексными числами.
Заголовочный файл mycomplex.h

#include <math.h>
namespace test
{
class Complex
{
public:
 // Конструктор по умолчанию
 Complex():itsRe(0),itsIm(0) { }
 // Конструктор с параметрами: действительная и мнимая часть
 Complex(double real, double imagine):itsRe(real),itsIm(imagine) { }
 // Конструктор копирования
 Complex(const Complex &);
 // Деструктор
 ~Complex() { itsRe = 0; itsIm = 0; }

 // Арифметические операции
 Complex operator+ (const Complex &);
 Complex operator- (const Complex &);
 Complex operator* (const Complex &);
 Complex operator/ (const Complex &);

 void operator+= (const Complex &);
 void operator-= (const Complex &);
 void operator*= (const Complex &);
 void operator/= (const Complex &);

 // Унарный оператор знака
 Complex operator- () const;

 // Операторы присваивания 
 Complex operator= (const Complex &);
 Complex operator= (const double &);

 // Функции доступа
 // Получить действительную часть 
 double GetReal() const { return itsRe; }
 // Получить мнимую часть
 double GetImagine() const { return itsIm; }
 
 // Модуль комплексного числа
 double GetAbs() const;
 // Аргумент комплексного числа
 double GetArg() const;
private:
 double itsRe;
 double itsIm;
};
}

mycomplex.cpp

#include "mycomplex.h"
namespace test{
Complex::Complex(const Complex & rhs)
{
 itsRe = rhs.itsRe;
 itsIm = rhs.itsIm;
}

Complex Complex::operator+ (const Complex & rhs)
{
 return Complex(itsRe + rhs.itsRe, itsIm + rhs.itsIm); 
}

Complex Complex::operator- (const Complex & rhs)
{
 return *this + (-rhs);
}

Complex Complex::operator* (const Complex & rhs)
{
 double re = (itsRe * rhs.itsRe) - (itsIm * rhs.itsIm);
 double im = (itsIm * rhs.itsRe) + (itsRe * rhs.itsIm);

 return Complex(re, im);
}

Complex Complex::operator/ (const Complex & rhs)
{
 double c = (rhs.itsRe * rhs.itsRe) + (rhs.itsIm * rhs.itsIm);
 double re = (itsRe * rhs.itsRe) + (itsIm * rhs.itsIm);
 double im = (itsIm * rhs.itsRe) - (itsRe * rhs.itsIm);

 return Complex(re/c, im/c); 
}

void Complex::operator+= (const Complex & rhs)
{
 *this = *this + rhs;
}

void Complex::operator-= (const Complex & rhs)
{
 *this = *this - rhs;
}

void Complex::operator*= (const Complex & rhs)
{
 *this = *this * rhs;
}

void Complex::operator/= (const Complex & rhs)
{
 *this = *this / rhs;
}

Complex Complex::operator- () const
{
 return Complex(-itsRe, -itsIm);
}

Complex Complex::operator= (const Complex & rhs)
{
 itsRe = rhs.itsRe;
 itsIm = rhs.itsIm;

 return *this;
}

Complex Complex::operator= (const double & rhs)
{
 itsRe = rhs;
 itsIm = 0;

 return *this;
}

double Complex::GetAbs() const
{
 return sqrt((itsRe * itsRe) + (itsIm * itsIm));
}

double Complex::GetArg() const
{
 if (itsRe > 0 || itsIm != 0)
  return 2 * atan( itsIm / (this->GetAbs() + itsRe) );
 else if (itsRe < 0 && itsIm == 0)
  return M_PI;
 
 return 0;
}
}

Пример использования. Для проверки корректности результатов проводится сравнение со встроенным в стандартную библиотеку классом Complex<T>.
#include "mycomplex.h"
#include <iostream>
#include <complex>


int main()
{
 test::Complex n2(2.0, 3.0);
 test::Complex n3 = n2+(-n2);
 std::cout << n2.GetReal() << ":" << n2.GetImagine() << "\n";
 std::cout << -n2.GetReal() << ":" << -n2.GetImagine() << "\n";
 std::cout << n3.GetReal() << ":" << n3.GetImagine() << "\n";

 std::cout << n2.GetArg() << ":" << n2.GetAbs() << "\n";

 std::complex<double> x(2., 3.);

 std::cout << std::arg(x) << ":" << std::abs(x) << "\n";

 return 0;
}