Последние записи
- Удаление папки с файлами
- Распечатка файла
- Преобразовать массив байт в вещественное число (single)
- TChromium (CEF3), сохранение изображений
- Как в Delphi XE обнулить таймер?
- Изменить цвет шрифта TextBox на форме
- Ресайз PNG без потери прозрачности
- Вывод на печать графического файла
- Взаимодействие через командную строку
- Перенести программу из Delphi в Lazarus
Интенсив по Python: Работа с API и фреймворками 24-26 ИЮНЯ 2022. Знаете Python, но хотите расширить свои навыки?
Slurm подготовили для вас особенный продукт! Оставить заявку по ссылке - https://slurm.club/3MeqNEk
Online-курс Java с оплатой после трудоустройства. Каждый выпускник получает предложение о работе
И зарплату на 30% выше ожидаемой, подробнее на сайте академии, ссылка - ttps://clck.ru/fCrQw
11th
Ноя
Организация кода
Posted by Chas under c/c++, Общалка, Статьи
Во-первых, если только у Вас нет веских причин держаться Борланда, уйдите с него. Дело в том, что господа Embarcadero пытались «усовершенствовать» C++, создав при этом заметно отличающийся диалект; часть этих наработок ушла в язык C#.
Как альтернативы, можно рассматривать Qt от Trolltech-Nokia или C++ express от Microsoft.
Abstraction
Во-вторых. В рамках одного проекта все современные среды разработки позволяют с лёгкостью использовать множественные файлы кода (Borland называет их Units). Это удобно, так как позволяет часть кода сосредоточить в одном «модуле», в который (если код в нём выверен и отлажен) заглядывать в дальнейшем не понадобится, а надо будет только вызывать функции. Кроме того, это позволяет скомпилировать код из файла в «почти» машинный код один раз, и лишь чуть-чуть менять при изменении прочих частей проекта, что сокращает время компиляции (не самый большой выигрыш на маленьких модулях, но привыкать полезно сразу). Однако кроме сгенерированного «почти машинного» кода, коль скоро мы компилируем модули поштучно, нужно уметь объяснять компилятору, что вызов такой-то функции, которой в текущем модуле нет, действие тем не менее легальное — она встретится в будущем.
Рассмотрим пример: мы собрались написать несколько полезных функций для вычислений в отдельном модуле. В файле MyMath.cpp пишем:
/*---------------------------------------------------------* Библиотека математических функций
* написана Abstraction, 03.11.2011
* лежит в пространстве имён MyMath
*--------------------------------------------------------*/
//Чтобы не думать о том, что выбранные мной названия случайно могут совпасть
//с системными, используется такая полезная штука, как пространство имён
namespace MyMath{
//Функция вычисления факториала
//Возвращает значение n!, если оно влезает в int
//Если же скормили слишком много или отрицательное число - возвращает -1
//(факториал от отрицательного целого числа неопределён)
//Поскольку факториал растёт ОЧЕНЬ быстро, проще не исхитряться с умножением в цикле,
//а сделать так:
//Создаём глобальный массив возможных значений (g_ в начале имени удобно приписывать
//всем глобальным переменным - так исчезает риск случайного совпадения имени с локальной переменной
int g_factorialValues[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880,
3628800, 39916800, 479001600};//Для 64-разрядного int допишите сами
//И саму функцию
int Factorial(int n){
if( n<0 || n>12 ) return -1;
return g_factorialValues[n];
}
//Биноминальный коэффициент, число сочетаний из n по k.
//Возвращает C(n, k) при n>=0, 0 при n<0, либо -1, если зафиксировано переполнение
//Реализуем через рекурсию
int Binominal(int n, int k){
//Если k вылетело за пределы [0, n], ответ 0
if(k<0 || k>n) return 0;
//Если n<0, по нашим соглашениям ответ 0
if(n<0) return 0;
//Если k=n или 0, коэфффициент равен 1
if(k==n || k==0) return 1;
//А в остальных случаях,
int c1=Binominal(n-1, k);
int c2=Binominal(n-1, k-1);
int ret = c1+c2;
//Проверка переполнения
if(ret
//Нам необходимо сказать компилятору: всё в порядке, есть среди прочих файлов проекта такие функции
int MyMath::Factorial(int n);
int MyMath::Binominal(int n, int k);
int main(void){
int sample;
std::cin >> sample;
std::cout << MyMath::Factorial(sample);
return 0;
}[/code]
Какие мы видим недостатки? Ну, во-первых, если я решу переименовать функцию в модуле MyMath, мне придётся шариться по всем прочим файлам проекта и проводить переименование.
Во-вторых, обратите внимание на комментарии к функциям: они явно распадаются на две части - как функцию использовать (то, что интересно при использовании модуля, интерфейс) и как она реализована (то, что интересно при написании модуля, реализация). Второе есть желание скрыть - какое дело использующему библиотеку, как именно я её создавал?
В-третьих, при этом любому пользователю модуля-библиотеки неизбежно открываются подробности, которые ему лучше бы не знать - например, информация о существовании g_factorialValues. От этого недалеко до того, чтобы обратиться к массиву самому, после чего я решу по какой-то причине переписать реализацию факториала, не меняя интерфейс и считая, что теперь достаточно только пересобрать проект - а вместо этого здравствуйте, ошибки в программе!
Поэтому библиотеку можно переписать, разбив на два файла:
MyMath.h
[code]/*---------------------------------------------------------
* Библиотека математических функций
* написана Abstraction, 03.11.2011
* лежит в пространстве имён MyMath
*--------------------------------------------------------*/
//Чтобы не думать о том, что выбранные мной названия случайно могут совпасть
//с системными, используется такая полезная штука, как пространство имён
namespace MyMath{
//Функция вычисления факториала
//Возвращает значение n!, если оно влезает в int
//Если же скормили слишком много или отрицательное число - возвращает -1
//(факториал от отрицательного целого числа неопределён)
int Factorial(int n);
//Биноминальный коэффициент, число сочетаний из n по k.
//Возвращает C(n, k) при n>=0, 0 при n<0, либо -1, если зафиксировано переполнение
int Binominal(int n, int k);
}[/code]
Это в чистом виде "заявки", ничего из них не компилируется в реальный код. И MyMath.cpp, который остаётся без изменений: при переделке реализации знать об интерфейсе необходимо, а вот в обратную сторону не обязательно.
Директива препроцессора #include "Имя файла" берёт текст файла и вставляет его вместо этой строки, это простая текстовая замена в исходном коде (как и большинство инструкций препроцессора). То есть, файл Program.cpp теперь видоизменился:
[code]#include
//Нам необходимо сказать компилятору: всё в порядке, есть среди прочих файлов проекта такие функции
#include "MyMath.h"
int main(void){
int sample;
std::cin >> sample;
std::cout << MyMath::Factorial(sample); return 0; }[/code] Среда разработки при добавлении нового .cpp - файла к проекту автоматически соображает, что его надо будет компилировать вместе с прочими, так что программисту достаточно выбрать пункт меню "добавить новый файл кода к проекту", или как-то там. Теперь пара замечаний. 1) Из-за того, что #include подставляет вместо себя текст файла, в заголовочных файлах категорически не рекомендуется использовать директиву using namespace. В cpp-файлах это делать можно, но после последней из директив #include. 2) При сложной структуре включений, может получиться так, что один и тот же заголовочный файл в данном модуле возникнет в двух экземплярах. Два одинаковых объявления - это ошибка, так что препроцессору в заголовочном файле надо как-то сказать "эй, если при обработке текущего (.cpp) файла меня уже видел, всё, второй раз подставлять не надо". Классическое решение - т.н. "стражи включения". В начало MyMath.h пишем [code]//Препроцессор! Если ты меня (__MYMATH_H) ещё не видел #ifndef __MYMATH_H //Запомни: меня зовут __MYMATH_H #define __MYMATH_H[/code] А в конце [code] //Всё, я кончился #endif[/code] Что это значит? То, что при первой встрече препроцессор сообразит, что имя __MYMATH_H ему незнакомо (нужно любое название макроса, которое заведомо не встретится ни в каком другом файле), условие ifndef (if-not-defined) выполнено и воспримет весь текст до #endif. А вот при второй встрече имя будет знакомо, и препроцессор сразу "проскочит" до #endif, пропуская весь текст файла. Могут быть доступны и другие способы - так, в Visual Studio достаточно написать в начале файла #pragma once. тема на форуме
Похожие статьи
Купить рекламу на сайте за 1000 руб
пишите сюда - alarforum@yandex.ru
Да и по любым другим вопросам пишите на почту
пеллетные котлы
Пеллетный котел Emtas
Наши форумы по программированию:
- Форум Web программирование (веб)
- Delphi форумы
- Форумы C (Си)
- Форум .NET Frameworks (точка нет фреймворки)
- Форум Java (джава)
- Форум низкоуровневое программирование
- Форум VBA (вба)
- Форум OpenGL
- Форум DirectX
- Форум CAD проектирование
- Форум по операционным системам
- Форум Software (Софт)
- Форум Hardware (Компьютерное железо)