Пользовательские типы
Ключевое слово typedef в языке C++ позволяет создавать пользовательские типы данных – для этого достаточно определить новое имя типа данных для уже существующего типа данных. При этом сам новый тип данных не создается, а лишь определяется новое имя для уже существующего типа. Благодаря использованию пользовательских типов можно делать программы более гибкими: для этого иногда достаточно изменить typedef-инструкции с помощью макросов подстановки (#define). Использование пользовательских типов позволяет также улучшить читабельность кода, поскольку для стандартных типов данных с помощью typedef можно использовать собственные описательные имена. Общий формат записи инструкции для создания пользовательского типа:
typedef тип новое_имя;
Здесь элемент тип означает любой допустимый тип данных, а элемент новое_имя – новое имя для этого типа. Важно отметить, что новое имя определяется только в качестве дополнения к существующему имени типа, а не для его замены. В языке MQL5 с помощью typedef можно создавать указатели на функции.
Указатель на функцию
Указатель на функцию в общем виде определятся форматом записи
typedef тип_результата_функции ( * Имя_типа_функции)(список_типов_входных параметров);
где после слова typedef задается сигнатура функции – количество и тип входных параметров, а также тип возвращаемого функцией результата. В качестве объяснения приведем простой пример создания и использования указателя на функцию:
//— объявим указатель на функцию, которая принимает два параметра типа int
typedef int (*TFunc)( int , int );
//— TFunc является типом и мы можем объявить переменную-указатель на функцию
TFunc func_ptr; // указатель на функцию
//— объявим функции, которые соответствуют описанию TFunc
int sub( int x, int y) < return (x-y); >// вычитание одного числа из другого
int add( int x, int y) < return (x+y); >// сложение двух чисел
int neg( int x) < return (~x); >// инвертирование битов в переменной
//— в переменную func_ptr можно сохранить адрес функции, чтобы в дальнейшем ее вызывать
func_ptr=sub;
Print (func_ptr(10,5));
func_ptr=add;
Print (func_ptr(10,5));
func_ptr=neg; // ошибка: neg не имеет тип int (int,int)
Print (func_ptr(10)); // ошибка: должно быть два параметра
В данном примере переменной func_ptr можно присвоить функции sub и add , поскольку они имеют по два входных параметра типа int, как это указано в определении указателя на функцию TFunc . А вот функция neg не может быть присвоена указателю func_ptr, так как ее сигнатура отличается.
Организации событийных моделей в пользовательском интерфейсе
С помощью указателей на функции удобно строить обработку событий при создании пользовательского интерфейса. Сначала определим указатель на функцию TAction , которая будет вызываться по нажатию кнопки, и создадим три функции в соответствии с описанием TAction.
Затем произведем класс MyButton от CButton, в котором добавим член TAction , являющийся указателем на функцию.
//+——————————————————————+
//| Создадим свой класс кнопки с функцией обработки событий |
//+——————————————————————+
class MyButton: public CButton
<
private :
TAction m_action; // обработчик событий графика
public :
MyButton( void )<>
~MyButton( void )<>
//— конструктор с указанием текста кнопки и указателя на функцию для обработки событий
MyButton( string text, TAction act)
<
Text(text);
m_action=act;
>
//— установка собственной функции, которая будет вызываться из обработчика событий OnEvent()
void SetAction(TAction act)
//— стандартный обработчик событий графика
virtual bool OnEvent( const int id, const long &lparam, const double &dparam, const string &sparam) override
<
if (m_action!= NULL & lparam==Id())
<
//— вызовем собственный обработчик m_action()
m_action(sparam,( int )lparam);
return (true);
>
else
//— вернем результат вызова обработчика из родительского класса CButton
return (CButton::OnEvent(id,lparam,dparam,sparam));
>
>;
Далее создадим производный класс CControlsDialog от CAppDialog, в котором добавим массив m_buttons для хранения кнопок типа MyButton , а также методы AddButton(MyButton &button) и CreateButtons().
//+——————————————————————+
//| Класс CControlsDialog |
//| Назвначение: графическая панель для управления приложением |
//+——————————————————————+
class CControlsDialog : public CAppDialog
<
private :
CArrayObj m_buttons; // массив кнопок
public :
CControlsDialog( void )<>;
~CControlsDialog( void )<>;
//— create
virtual bool Create( const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2) override ;
//— добавление кнопки
bool AddButton(MyButton &button)< return (m_buttons.Add( GetPointer (button)));m_buttons.Sort();>;
protected :
//— создание кнопок
bool CreateButtons( void );
>;
//+——————————————————————+
//| Создание объекта CControlsDialog на графике |
//+——————————————————————+
bool CControlsDialog::Create( const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2)
<
if (!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))
return ( false );
return (CreateButtons());
//—
>
//+——————————————————————+
//| defines |
//+——————————————————————+
//— indents and gaps
#define INDENT_LEFT (11) // indent from left (with allowance for border width)
#define INDENT_TOP (11) // indent from top (with allowance for border width)
#define CONTROLS_GAP_X (5) // gap by X coordinate
#define CONTROLS_GAP_Y (5) // gap by Y coordinate
//— for buttons
#define BUTTON_WIDTH (100) // size by X coordinate
#define BUTTON_HEIGHT (20) // size by Y coordinate
//— for the indication area
#define EDIT_HEIGHT (20) // size by Y coordinate
//+——————————————————————+
//| Создание и добавление кнопок на панель CControlsDialog |
//+——————————————————————+
bool CControlsDialog::CreateButtons( void )
<
//— расчет координат кнопок
int x1=INDENT_LEFT;
int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);
int x2;
int y2=y1+BUTTON_HEIGHT;
//— добавим объекты кнопок вместе с указателями на функции
AddButton( new MyButton( «Open» ,Open));
AddButton( new MyButton( «Save» ,Save));
AddButton( new MyButton( «Close» ,Close));
//— создадим кнопки графически
for ( int i=0;i
MyButton *b=(MyButton*)m_buttons.At(i);
x1=INDENT_LEFT+i*(BUTTON_WIDTH+CONTROLS_GAP_X);
x2=x1+BUTTON_WIDTH;
if (!b.Create(m_chart_id,m_name+ «bt» +b.Text(),m_subwin,x1,y1,x2,y2))
<
PrintFormat ( «Failed to create button %s %d» ,b.Text(),i);
return ( false );
>
//— добавим каждую кнопку в контейнер CControlsDialog
if (!Add(b))
return ( false );
>
//— succeed
return (true);
>
Теперь мы можем написать программу с использованием панели управления CControlsDialog, в которой создается 3 кнопки «Open», «Save» и «Close». При нажатии на кнопку вызывается соответствующая ей функция, которая прописана в виде указателя на функцию TAction .
Внешний вид запущенного приложения и результаты нажатия кнопок представлены на картинке.

Полный исходный код программы
Структурный тип данных
Примером структуры может послужить любой объект, для которого описывается ряд его характеристик, имеющих значение в данной программе. Например, для книг это может быть название, автор, количество страниц; для окружности — координаты центра, диаметр, цвет. На языке программирования C объявление вышеупомянутых структурных типов данных может выглядеть так:
struct book char title[50]; char author[30]; int pages; >; struct circle int x, y; float dia; char color[10]; >;
В данном случае мы как бы создаем новый тип данных, но еще не объявляем переменных этих типов. Обратите внимание на точку с запятой в конце объявлений.
Чаще переменные структур объявляются так:
struct circle a, b, c; struct book mybook;
Здесь объявляются три структуры типа circle и одна структура типа book. Можно объявлять типы структур и их переменные по-иному, но мы для избежания путаницы рассматривать другие способы не будем.
Каждая переменная типа circle содержит четыре элемента (или поля) — x, y, dia, color. Можно сказать, что они представляют собой вложенные переменные. Причем эти переменные разных типов. Таким образом переменная-структура позволяет объединить под одним именем ряд разнородных данных. Обычно это нужно для удобства обработки данных. Если нельзя было бы создавать структуры, то пришлось бы создавать множество независимых переменных или ряд массивов, явной взаимосвязи между которыми не было бы. Структуры же позволяют объединять взаимосвязанные данные. Это конечно еще не объектно-ориентированное программирование, но уже взгляд в его сторону.
Объявив переменную структурного типа, мы можем получить доступ к каждому ее элементу для присваивания, изменения или получения значения:
a.x = 10; a.dia = 2.35; printf("%.2f ", a.dia);
Значение элементов структуры можно сразу определять при объявлении переменной, что похоже на инициализацию массивов:
struct book lang_c = {"Language C", "Ritchi", 99};
Значение переменной-структуры можно присвоить переменной того же типа:
struct book { char *title, *author; int pages; }; struct book old, new; old.title = "GNU/Linux"; old.author = "people"; old.pages = 20213; new = old; new.pages += 2000; printf("%d, %d\n", old.pages, new.pages);
В четвертой строке кода данные переменной old присваиваются new. В итоге вторая структура содержит копию данных первой. То, что можно выполнять присваивание по отдельным полям должно быть понятно.
Структуры и функции
Структуры-переменные можно передавать в функции в качестве параметров и возвращать их оттуда. Структуры передаются по значению, как обычные переменные, а не по ссылке, как массивы.
Рассмотрим программу, в которой одна функция возвращает структуру, а другая — принимает ее в качестве параметра:
#include #include struct circle { int x, y; float dia; char color[10]; }; struct circle new_circle(); void cross (struct circle); int main () { struct circle a; a = new_circle(); cross(a); } struct circle new_circle() { struct circle new; printf("Координаты: "); scanf("%d%d", &new.x, &new.y); printf("Диаметр: "); scanf("%f", &new.dia); printf("Цвет: "); scanf("%s", new.color); return new; } void cross (struct circle c) { double hyp; hyp = sqrt((double) c.x * c.x + (double) c.y * c.y); printf("Расстояние: %.2lf\n", hyp); if (hyp c.dia / 2) puts("Пересекает"); else puts("Не пересекает"); }
Примечание. При компиляции программы в GNU/Linux команда выглядит так: gcc program.c -lm . Это связано с использованием библиотеки с математическими функциями.
- Объявляется структура circle как глобальный тип данных. Таким образом любая, а не только main() , функция может создавать переменные этого типа.
- Функция new_circle() возвращает структуру, а функция cross() принимает структуру по значению. Следует отметить, что можно создавать функции, которые как принимают (возможно, несколько структур) так и возвращают структуру.
- В функции new_circle() создается переменная new типа struct circle, поля которой заполняются пользователем. Функция возвращает значение переменной new в функцию main() , где это значение присваивается переменной a, которая также принадлежит типу sctruct circle.
- Функция cross() определяет, пересекает ли круг начало координат. В ее теле вычисляется расстояние от центра круга до начала координат. Это расстояние является гипотенузой прямоугольного треугольника, длина катетов которого равна значениям x и у. Далее, если гипотенуза меньше радиуса, то круг пересекает начало координат, т.е. точку (0, 0).
- В функции main() при вызове cross() данные, содержащиеся в переменной a, копируются и присваиваются переменной c.
Указатели и структуры
В отличие от массивов, структуры передаются в функции по значению. Это не всегда рационально, т.к. структуры могут быть достаточно большого размера, и копирование таких участков памяти может замедлять работу программы. Поэтому часто структуры в функцию передают по ссылке, при этом можно использовать как указатель, так и операцию получения адреса.
// переменная-структура struct book new; // указатель на структуру struct book *pnew; // передаем адрес reader(&new); pnew = &new; // передаем указатель reader(pnew);
Тогда функция reader() должна иметь примерно такое объявление:
void reader(struct book *pb);
Возникает вопрос, как при использовании указателей обращаться к элементам структур? Во первых надо получить саму структуру, т.е. если pnew указатель, то сама структура будет *pnew . Далее можно уже обращаться к полям через точку: *pnew.title . Однако это выражение не верно, т.к. приоритет операции «точка» (обращение к полю) выше операции «звездочка» (получить значение по адресу). Таким образом приведенная запись сначала пытается получить значение поля title у указателя pnew, но у pnew нет такого поля. Проблема решается с помощью скобок, которые изменяют порядок выполнения операций: (*pnew).title . В таком случае сначала извлекается значение по адресу pnew, это значение является структурой. Затем происходит обращение к полю структуры.
В языке программирования C записи типа (*pnew).title часто заменяют на такие: pnew->title , что позволяет синтаксис языка. Когда в программе вы видите стрелку (тире и скобка) всегда помните, то, что написано до стрелки, — это указатель на структуру, а не переменная-структура.
Пример кода с использованием указателей:
#include struct circle { int x, y; float dia; }; void inversion (struct circle *); int main () { struct circle cir, *pc = ○ pc->x = 10; pc->y = 7; pc->dia = 6; inversion(pc); printf("x = %d, y = %d\n", cir.x, cir.y); } void inversion(struct circle *p) { p->x = -p->x; p->y = -p->y; }
Массивы структур
Обычно создание в программе одной переменной структурного типа не имеет особого смысла. Чаще структурами пользуются, когда необходимо описать множество похожих объектов, имеющих разные значения признаков. Значения каждого объекта следует объединить вместе (в структуру) и тем самым отделить от значений других объектов. Например, описание ряда книг или множества людей. Таким образом мы можем организовать массив, где каждый элемент представляет собой отдельную структуру, а все элементы принадлежат одному и тому же структурному типу.
Напишем программу для учета персональных компьютеров в организации. Каждая структура будет описывать определенные модели и содержать поле, в котором будет указано количество таких объектов. Поэтому при объявлении структурного типа данных следует описать такие поля как тип компьютера, модель процессора, количество.
Программа будет предоставлять возможность получать информацию о всех моделях и изменять количество компьютеров указанной пользователем модели. В программе будут определены две функции (помимо main() ): для вывода всей информации и для изменения количества компьютеров.
#include #define N 5 struct computer { char *type; char *proc; int qty; }; void viewer (struct computer *); void changer (struct computer *); int main () { struct computer comps[N]= { "Desktop", "earlier then P4", 10, "Desktop", "P4", 30 , "Desktop", "Core", 20 , "Desktop", "AMD", 2 , "Notebook", "Core", 1 }; viewer(comps); changer(comps); viewer(comps); } void viewer (struct computer *comp) { int i; for (i=0; i N; i++, comp++) printf("%2d) %-8s - %-15s: %3d\n", i+1, comp->type, comp->proc, comp->qty); } void changer (struct computer *comp) { int i, differ; printf("Введите номер модели: "); scanf("%d", &i); i--; printf( "На сколько уменьшить или увеличить: "); scanf("%d", &differ); (comp+i)->qty += differ; }
- Массив структур инициализируется при его объявлении.
- Функции viewer() и changer() принимают указатели на структуру computer.
- В теле viewer() указатель инкрементируется в заголовке цикла; таким образом указывая на следующий элемент массива, т.е. на следующую структуру.
- В выражении (comp+i)->qty скобки необходимы, т.к оператор -> имеет более высокий приоритет. Скобки позволяют сначала получить указатель на i-ый элемент массива, а потом обратиться к его полю.
- Декрементирование i в функции changer() связано с тем, что индексация начинается с нуля, а номера элементов массива, которые пользователь видит на экране, с единицы.
- Для того, чтобы уменьшить количество компьютеров, при запросе надо ввести отрицательное число.
Пример результата работы программы:

Придумайте свою программу, в которой бы использовался массив структур, или перепишите приведенную выше программу и дополните ее функцией, которая позволяет добавлять в массив новый элемент-структуру.
Курс с решением части задач:
pdf-версия
Создать свой тип данных
Не получается создать свой тип данных
Всем привет! Подскажите почему компилятор ругается на эти три строчки? MyVector2D():x(0),y(0)<>.
Создать свой целочисленный тип данных
Здравствуйте, у меня возник вопрос, можно ли создать например целочисленный тип данных, который.

Как создать свой целочисленный тип данных с пределом от 0 до 500
Доброе утро. Кто может подсказать, как создать персональный тип данных. Задумка вот в чем.
Как создать свой собственный тип на C++?
Здравствуйте. Возник вопрос как создать свой собственный тип на С++ который включат такие типы.
![]()
![]()
5277 / 2364 / 342
Регистрация: 20.02.2013
Сообщений: 5,763
Записей в блоге: 20
IlyaMakyha, нет, так невозможно. Только так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#include class Bignum { std::string digits; // Число }; int main() { Bignum b("@4487612165"); // или b = "@133+@559098"; return 0; }
18413 / 9584 / 2341
Регистрация: 30.01.2014
Сообщений: 16,742
Сообщение от IlyaMakyha 
Возможно ли сделать вот так:
А зачем?
В смысле зачем собачки перед литералами?
Можно же просто
1 2 3
Bignum b(4487612165); b = 133 + 559098;
для этого необходимо будет перегрузить конструктор и оператор присваивания для соответствующих целых типов.
Если же нужно будет задавать целое число, которое не помещается в стандартные типы, то да, в виде строки придется задавать, затем переводить ее во внутреннее представление твоего класса.
![]()
![]()
5277 / 2364 / 342
Регистрация: 20.02.2013
Сообщений: 5,763
Записей в блоге: 20
Сообщение от DrOffset 
перегрузить конструктор и оператор присваивания
Сообщение от DrOffset 
задавать целое число, которое не помещается в стандартные типы, то да, в виде строки придется задавать
DrOffset, опередил меня )))
IlyaMakyha, как-то так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
#include #include class Bignum { std::string digits_; public: Bignum() { digits_ = ""; } explicit Bignum( std::string d ) : digits_( d ) {} explicit Bignum( const char* d ) : digits_( d ) {} Bignum( const Bignum& b ) { digits_ = b.digits_; } Bignum& operator=( const Bignum& b ) { if ( this != &b ) digits_ = b.digits_; return *this; } Bignum& operator=( const std::string& b ) { if ( &digits_ != &b ) digits_ = b; return *this; } friend void operator( std::ostream& os, const Bignum& b ) { os b.digits_ "\n"; } }; int main() { Bignum b( "@4487612165" ); std::cout b; // или b = "@133+@559098"; std::cout b; return 0; }
Любитель чаепитий
3741 / 1798 / 565
Регистрация: 24.08.2014
Сообщений: 6,016
Записей в блоге: 1
Сообщение от gru74ik 
1 2 3 4
friend void operator( std::ostream& os, const Bignum& b ) { os b.digits_ "\n"; }
Вроде возвращают os, а не void?
![]()
![]()
5277 / 2364 / 342
Регистрация: 20.02.2013
Сообщений: 5,763
Записей в блоге: 20
GbaLog-, можно как я написал, если цепочки вызовов не планируешь делать.
Добавлено через 3 минуты
GbaLog-, хотя, конечно, каноничный вариант такой:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
#include #include class Bignum { std::string digits_; public: Bignum() { digits_ = ""; } explicit Bignum( std::string d ) : digits_( d ) {} explicit Bignum( const char* d ) : digits_( d ) {} Bignum( const Bignum& b ) { digits_ = b.digits_; } Bignum& operator=( const Bignum& b ) { if ( this != &b ) digits_ = b.digits_; return *this; } Bignum& operator=( const std::string& b ) { if ( &digits_ != &b ) digits_ = b; return *this; } friend std::ostream& operator( std::ostream& os, const Bignum& b ) { os b.digits_ "\n"; return os; // Бодхидхарма одобряет } }; int main() { Bignum b( "@4487612165" ); std::cout b; b = "@133+@559098"; Bignum c( "O-LO-LO-666" ); std::cout b c; // дух Дзен с домино и блудницами return 0; }
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
Помогаю со студенческими работами здесь
Как реализовать свой тип данных
Здравсвтуйте,подскажите пожалуйста как реализовать с с++ свой тип данных. Допустим хочу завести.
Свой тип данных для перегрузки функций
Добрый день! Имеется широта и долгота — все FLOAT. Хочу сделать пару функций с одинаковым.

Свой тип данных позволяющий хранить числа разрядностью в Доброго времени суток! Возникла потребность иметь программу позволяющую производить.
Создать свой поток данных
Не могу понять, возможно ли как то создать свой поток данных? Знаю есть файловые потоки, cin cout.
Как сконвертировать свой тип в тип double?
Можно ли конвертировать свой тип в тип doble? Если да, то каким способом?
Создать тип данных Многоразрядное число
Создать тип данных Многоразрядное число. Разработать следующие функции: • Equal() – сравнение.
#2 — Переменные и типы данных в Си
Переменные являются важной частью любого языка программирования. В ходе урока мы познакомимся с написанием переменных, а также изучим различные типы данных. Также мы научимся выводить и получать данные от пользователя.
Видеоурок
Переменные невероятно важны, так как позволяют хранить информацию и использовать её в дальнейшем. Вначале может быть не совсем понятно зачем вообще что-то записывать в переменную, если можно просто оперировать значениями без них. Понимание переменных придет немного позже, когда мы начнем создавать более сложные программы и нам потребуется хранить информацию в каком-либо месте.
Типы переменных в языке Си указываются перед названием переменной. От конкретного типа данных будет зависеть содержимое переменной. Если мы укажем что переменная с типом данных для целых чисел int , то в неё мы не сможем записать строку или число с точкой.
В ходе программы мы можем записывать новые значения в переменную, но тип данных должен оставаться неизменным:
float some_value = 2.95; some_value = 6.9; // Записали новое значение
Рассмотрим все типы данных для создания переменных.
Целые числа
- short — предназначен для хранения целых чисел. Диапазон чисел от -32 768 до 32 767;
- unsigned short — предназначен для хранения целых положительных чисел. Диапазон чисел от 0 до 65 535;
- int — предназначен для хранения целых чисел. Диапазон чисел от -2 147 483 648 до 2 147 483 647;
- unsigned int — предназначен для хранения целых положительных чисел. Диапазон чисел от 0 до 4 294 967 295;
- long — предназначен для хранения целых чисел. Диапазон чисел от –9 223 372 036 854 775 808 до 9 223 372 036 854 775 807;
- unsigned long — предназначен для хранения целых положительных чисел. Диапазон чисел от 0 до 18 446 744 073 709 551 615.
Разница между типами заключается только в диапазоне чисел, который можно записать в переменную. Также не используйте большие типы данных по типу long, если число маленькое, так как чем больше диапазон, тем больше памяти требуется компьютеру на конкретную переменную.
Числа с точкой
- float — для создания чисел с плавающей точкой. Диапазон чисел от от -3.4*10 38 до 3.4*10 38 ;
- double — для создания чисел с плавающей точкой. Диапазон чисел от от ±4.9*10 -324 до ±1.8*10 308 .
Прочие типы данных
- bool — логический тип данных. Предназначен для записи истинного (true) или ложного (false) значения;
- char — тип данных для работы с символами. Позволяет поместить в одинарных кавычках какой-либо символ.
Для создания переменной необходимо указать тип данных и назвать её. Также можно сразу же добавить к ней значение:
int a = 0; // Добавление значения сразу float c; // Создание переменной без значения short b, y = 342; // Создание нескольких переменных
Для вызова переменной вы можете обратиться к ней по имени.
Получение данных
Для получения данных от пользователя необходимо воспользоваться функцией scanf() :
scanf("%d", &some_value);
Таким образом введенные пользователем данные будут помещены в переменную с названием «some_value».
Весь код будет доступен после подписки на проект!
Задание к уроку
Необходимо оформить подписку на проект, чтобы получить доступ ко всем домашним заданиям
Большое задание по курсу
Вам необходимо оформить подписку на сайте, чтобы иметь доступ ко всем большим заданиям. В задание входит методика решения, а также готовый проект с ответом к заданию.
PS: подобные задания доступны при подписке от 1 месяца