Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.72/25: Рейтинг темы: голосов - 25, средняя оценка - 4.72
0 / 0 / 0
Регистрация: 27.11.2014
Сообщений: 20

Освобождение динамической памяти

25.12.2018, 16:19. Показов 4785. Ответов 23
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Добрый день, объясните, пожалуйста, как правильно удалять выделенную динамическую память? Вот пример задачи, где массив произвольного размера может быть введен пользователем или взят из файла. Соответственно при вызове соответствующей функции выделяется память, массив вводится и сохраняется в выделенной памяти, чтобы его можно было вывести на экран. Если после этого снова вызывается функция ввода массива пользователем/из файла, то ранее выделенная память должна быть удалена, а новая выделиться, но компилятор мою интерпретацию такой логики не поддерживает.

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

C++
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include <iostream>
#include <cstring>
#include <conio.h>
#include <fstream>
 
using namespace std;
 
void menu()
{
    cout << "1 - Ввести массив с клавиатуры"<<endl;
    cout << "2 - Ввести массив из файла" << endl;
    cout << "3 - Вывести массив на экран" << endl;
}
 
int* Input(int Arsize)
{
    int *arr = new int[Arsize];
 
    for (int i(0); i < Arsize; i++)//заполнение массива
    {
        cout << "Введите " << i+1 << "-ый элемент массива" << endl;
        cin >> arr[i];
    }
    return arr;
}
 
void Output(int * Arr, int arsize)
{
    for (int i(0); i < arsize; i++)//вывод массива
    {
        cout << *(Arr + i) << " ";
    }
    cout << endl;
}
 
 
int main()
{
 
    using namespace std;
    setlocale(LC_ALL, "Russian");
    int* massiv=NULL;
    int size;
    int temp;
    char choice;
 
    ofstream fout;
    ifstream fin;
 
    do
    {
        system("cls");
        menu();//вызов меню
 
        cin >> choice;
        switch (choice)
        {
        case '1': //ввод массива с клавиатуры
        {
            delete [] massiv;
            cout << "введите размерность массива" << endl;
            cin >> size;
            massiv = Input(size);
            break;
        }
 
        case '2':
        {
            delete [] massiv;
            fin.open("textfile.txt");
            if (!fin.is_open())
            {
                cout << "файл не найден";
                _getch();
                break;
            }
 
            int i = 0;
            while (!fin.eof()) //сосчитаем кол-во членов в массиве в текстовом файле
            {
                fin >> temp;
                i++;
                size=i;
            }
            //cout << size << endl; //вывод количества файлов в массиве
 
            int *massiv = new int[size];
            //massiv = NULL;
 
            fin.seekg(0);//перевод каретки в начало строки
 
            for (int i(0); i < size; i++)
            {
                fin >> massiv[i];
                cout << massiv[i] << " ";
            }
 
            _getch();
            break;
        }
 
        case '3': //вывод массива на экран
        {
            system("cls");
            if (massiv == NULL)
                cout << "массив пустой" << endl;
            else
                Output(massiv, size);
            _getch();
            break;
        }
 
    } while (choice !='q');
 
    return 0;
}
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
25.12.2018, 16:19
Ответы с готовыми решениями:

Освобождение динамической памяти
После первого вызова функции(при повторном) пишет ошибку. В функции sort один из элементов массива становится недоступным для чтения. Если...

Освобождение динамической памяти
Здравствуйте! Была задача реализовать сложение длинных чисел с помощью стеков. Сам стек реализовал просто, но всё же проблемы есть -...

Освобождение динамической памяти в vs
Вообщем попался я тут, и так, код: //#define _CRT_SECURE_NO_WARNINGS добавить в vs #include &lt;iostream&gt; #include...

23
 Аватар для COKPOWEHEU
4083 / 2681 / 432
Регистрация: 09.09.2017
Сообщений: 11,913
25.12.2018, 17:02
87 строка: создаете локальную переменную, перекрывающую глобальную. Выделяете под нее память, но не освобождаете.
60, 69 строки: возможна попытка освобождения невыделенной памяти
17 строка: не проверяете успешность выделения памяти (грубая ошибка)
116 строка: не освобождаете память в конце программы
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
25.12.2018, 17:11
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
17 строка: не проверяете успешность выделения памяти (грубая ошибка)
Это не ошибка, это системная функция.
1) Стандартный new в случае ошибки не возвращает nullptr, а выбрасывает исключение.
2) Выбрасывает если повезет. Ибо если используется overcommit, об ошибке вы узнаете лишь при попытке записать в "выделенную" память.
0
0 / 0 / 0
Регистрация: 27.11.2014
Сообщений: 20
25.12.2018, 17:56  [ТС]
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
87 строка: создаете локальную переменную, перекрывающую глобальную. Выделяете под нее память, но не освобождаете.
А есть что-то плохое в том, что переменная в процессе программы приобретает другое значение? Объявить massiv перед case было необходимо, потому что если объявлять ее внутри одной метки, внутри других она видна не будет.
Как правильно освободить память? Нельзя же удалить её сразу после заполнения массива

Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
60, 69 строки: возможна попытка освобождения невыделенной памяти
Добавляю вот такую проверку
C++
1
2
    if (!(massiv == NULL))
        delete[] massiv;
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
17 строка: не проверяете успешность выделения памяти (грубая ошибка)
Как это сделать?

Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
116 строка: не освобождаете память в конце программы
Добавил в конце
C++
1
2
3
4
} while (choice !='q');
    return 0;
    delete[] massiv;
}
Если пытаться записать из файла после заполнения с клавиатуры, то с первой попытки массив из файла выводится, со второй выводится абракадабра, а на третьей ничего не происходит. если сразу записывать из файла, срабатывает ошибка как раз на второй метке
C++ (Qt)
1
delete[] massiv;
0
 Аватар для COKPOWEHEU
4083 / 2681 / 432
Регистрация: 09.09.2017
Сообщений: 11,913
25.12.2018, 18:24
Цитата Сообщение от Renji Посмотреть сообщение
1) Стандартный new в случае ошибки не возвращает nullptr, а выбрасывает исключение.
Я не говорил "проверять на nullptr", я говорил "проверять успешность выделения".
Какая разница, будет ли это if или try-catch? Все равно программа не может продолжать выполнение если не смогла выделить память. Такие ошибки надо отлавливать всегда.
Цитата Сообщение от kousnetsov Посмотреть сообщение
А есть что-то плохое в том, что переменная в процессе программы приобретает другое значение? Объявить massiv перед case было необходимо, потому что если объявлять ее внутри одной метки, внутри других она видна не будет.
Само по себе это не плохо, вот только вы пользуетесь данной возможностью неправильно. Между метками виден тот massiv, который глобальный, а не тот который локальный. Это две разные переменные. Просто удалите объявление локальной.
Цитата Сообщение от kousnetsov Посмотреть сообщение
Как правильно освободить память? Нельзя же удалить её сразу после заполнения массива
Как обычно: удаляем как только она стала ненужной. Либо перед выделением нового массива взамен старого, либо при выходе из программы.
Цитата Сообщение от kousnetsov Посмотреть сообщение
Как это сделать?
Для выделения памяти в стиле Си:
C
1
2
3
4
massiv = (int*)malloc(sizeof(int)*Arsize);
if(massiv == NULL){/*ошибка!*/}
...
free(massiv);
В стиле С++:
C++
1
2
3
4
5
try{
  massiv = new int[Arsize];
}catch(std::bad_alloc){
  /*ошибка*/
}
на С++ в стиле Си:
C++
1
2
noexcept(massiv = new int[Arsize]);
if( massiv == NULL ){/*Ошибка*/}
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
25.12.2018, 18:33
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
Какая разница, будет ли это if или try-catch? Все равно программа не может продолжать выполнение если не смогла выделить память. Такие ошибки надо отлавливать всегда.
Разница в том, что не обработанные исключения и так уже гарантированно вызывают std::terminate. Минимальная обработка таких ошибок уже сделана компилятором. А не минимальная в данном случае непонятно кому и зачем нужна.
0
 Аватар для COKPOWEHEU
4083 / 2681 / 432
Регистрация: 09.09.2017
Сообщений: 11,913
25.12.2018, 19:32
Угу. А потом в более сложной программе не возникнет даже мысли поставить проверку, поскольку привычки нет. И будет ТС искать проблему где угодно, но не в операторе new, или недооткрытом файле, который точно так же поленился проверить, или еще в каком не менее дурацком месте.
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
25.12.2018, 20:35
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
Угу. А потом в более сложной программе не возникнет даже мысли поставить проверку, поскольку привычки нет. И будет ТС искать проблему где угодно, но не в операторе new, или недооткрытом файле, который точно так же поленился проверить, или еще в каком не менее дурацком месте.
Давайте с ТЗ определимся.
Если стоит задача найти проблему с отладчиком, он сам подсветит необработанный throw.
Если стоит задача найти проблему без отладчика, надо минимум стек-трейс делать. Потому как что толку от диагностического сообщения "где-то вылетел bad_alloc, подробности неизвестны"? Но стек-трейс это для топикстартера явно слишком круто.
Если же стоит задача просто аварийно закрыть программу, так это уже компилятор сделал.
0
 Аватар для COKPOWEHEU
4083 / 2681 / 432
Регистрация: 09.09.2017
Сообщений: 11,913
25.12.2018, 23:16
Моя задача в данном случае - приучить ТСа всегда делать проверку возвращаемого значения, всегда явно освобождать память и закрывать открытые ресурсы (вроде файлов). Если это дойдет до автоматизма на учебных примерах, он не будет отвлекаться на подобную рутину в будущем. А это чуть более информативные сообщения об ошибках программы вроде "программа упала степенно завершилась, поскольку не удалось выделить 100500 ГБ памяти. Временные файлы удалены, сетевые подключения закрыты"
Сколько раз уже видел здесь как новички даже открытие файла не проверяют, а потом жалуются "почему программа не работает". А она просто входной файл не там ищет.
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
26.12.2018, 00:10
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
Моя задача в данном случае - приучить ТСа всегда делать проверку возвращаемого значения, всегда явно освобождать память и закрывать открытые ресурсы (вроде файлов).
Для закрытия файлов деструктор есть. Опять же, вызываемый при броске исключения автоматически. Разумеется, если функция использует возвращаемое значение чтобы рапортовать об ошибке, это значение надо проверять. Но функции полагающиеся на механизм исключений, несколько на иной механизм обработки ошибок рассчитаны. И не надо пытаться забивать шурупы молотком.
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
Сколько раз уже видел здесь как новички даже открытие файла не проверяют, а потом жалуются "почему программа не работает". А она просто входной файл не там ищет.
Это не проблема новичков. Это проблема кривого дизайна std::ifstream, который рапортует об ошибке конструктора через флаг ошибки. Тогда как люди нормальные (например, писавшие std::vector) при сбое конструктора кидают исключение.
0
 Аватар для COKPOWEHEU
4083 / 2681 / 432
Регистрация: 09.09.2017
Сообщений: 11,913
26.12.2018, 00:44
Программа может захватывать не только std::ifstream файлы. И далеко не для все из них оформлены в виде объектов с деструкторами.
Цитата Сообщение от Renji Посмотреть сообщение
Это не проблема новичков. Это проблема кривого дизайна std::ifstream
Да они ничего не проверяют. Ни std::ifstream, ни new, ни fopen, ни malloc. Проблема тут не в дизайне конкретного ресурса, а в самом подходе.
Цитата Сообщение от Renji Посмотреть сообщение
Тогда как люди нормальные (например, писавшие std::vector) при сбое конструктора кидают исключение.
Исключения далеко не самый эффективный способ обработки ошибок. Та же проверка возвращаемого значения гораздо быстрее и проще.
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
26.12.2018, 01:14
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
Программа может захватывать не только std::ifstream файлы. И далеко не для все из них оформлены в виде объектов с деструкторами.
Ну, Си-стайл код это отдельная песня. Хотя, ничто не мешает обернуть fopen в файловый класс.
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
Исключения далеко не самый эффективный способ обработки ошибок. Та же проверка возвращаемого значения гораздо быстрее и проще.
Как раз сложнее.
Во-первых, что будем делать с ошибками в функциях типа std::log? Проверять возвращаемое значение - нельзя впихнуть std::log в выражение. Использовать errno - глобальная переменная и проблемы с потокобезопасностью (да, errno сделали thread_local, но он не бесплатен).

Во-вторых, обработка ошибок это по большей части просто "заверши функцию и перебрось код ошибки дальше". И только пять return спустя исполнение добирается до обработчика ошибок рисующего красивое окошко "что-то сбойнуло". Исключения это перебрасывание делают сами, а с кодами ошибки лишняя писанина.

А скорость - зависит от реализации обработчика исключений. Его можно делать и в стиле "все накладные расходы переносятся на момент возбуждения ошибки" (а этот момент возможно и не настанет никогда).
0
 Аватар для COKPOWEHEU
4083 / 2681 / 432
Регистрация: 09.09.2017
Сообщений: 11,913
26.12.2018, 08:25
Как минимум, в "красивом окошке" написать где именно возникла ошибка. Какой именно файл недоступен, сколько именно памяти не хватило.
Если ошибка возникла во время записи логов (что с ними вообще может случиться, файл что ли не создается?) напрямую сообщить пользователю что пошло не так и как это исправить.
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
26.12.2018, 17:00
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
Как минимум, в "красивом окошке" написать где именно возникла ошибка. Какой именно файл недоступен, сколько именно памяти не хватило.
Это все надо писать не в окошке, а в std::exception::what. А окошко должно лишь показывать что в what написано. Другой вопрос, что стандартные исключения не пишут туда ничего кроме своего названия. Вот здесь собственно мы и подходим к написанию трассировки стека (место ошибки сохранять) и запихиванию ее в конструктор исключения. Ну или как минимум:
C++
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
#include<iostream>
#include<cstdio>
#include<exception>
 
#define ERROR_POS __FILE__,__LINE__,__FUNCTION__
class MyException:public std::exception
{
public:
    MyException(const char*file,int line,const char*function)
    {
        snprintf(data,1024,"error in file %s line %d function %s",file,line,function);
    }
 
    const char*what()const noexcept{return data;}
 
    char data[1024];
};
 
int main()
try
{
    throw MyException(ERROR_POS);
    return 0;
}
catch(const std::exception&error)
{
    std::cout<<error.what()<<std::endl;
}
0
 Аватар для COKPOWEHEU
4083 / 2681 / 432
Регистрация: 09.09.2017
Сообщений: 11,913
27.12.2018, 10:26
Цитата Сообщение от Renji Посмотреть сообщение
окошко должно лишь показывать что в what написано
Нет-нет, в окошке должна быть только простейшая информация для пользователя если он может что-то исправить. Проверить правда доступа, наличие памяти, сети, некорректные данные и т.д., возможно с адресом лога. А вот уже в лог сбрасывать основную информацию для разработчика.
Разумеется, я говорю об общих принципах обработки ошибок. Какой конкретно механизм при этом будет использоваться - проверка возвращаемого значения, проверка errno, отлов исключений - безразлично.
Возвращаясь к теме дискуссии, я настаиваю на обязательной проверке всего что может сломаться, особенно в учебных программах. Ну, разве что арифметику уж слишком сложно проверять каждый раз, да и вероятность такой ошибки невелика.
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
27.12.2018, 16:27
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
Возвращаясь к теме дискуссии, я настаиваю на обязательной проверке всего что может сломаться, особенно в учебных программах.
Возвращаясь к теме дискуссии, проверить корректность выделения памяти в современных условиях де-факто просто невозможно. Не знаю как Винда, но Линукс по умолчанию позволяет выделять памяти больше чем ее есть физически (оверкоммит). Типа, с прицелом на программы хапающие много памяти "про запас", но по факту не использующие хапнутое. При попытке в эту память что-то записать приходит OOM Killer и убивает кого попало. И никак ты это не отследишь.
1
Mental handicap
 Аватар для Azazel-San
1246 / 624 / 171
Регистрация: 24.11.2015
Сообщений: 2,429
27.12.2018, 17:19
Цитата Сообщение от Renji Посмотреть сообщение
Не знаю как Винда, но Линукс по умолчанию позволяет выделять памяти больше чем ее есть физически (оверкоммит).
Да, на винде аналогично.
Цитата Сообщение от Renji Посмотреть сообщение
приходит OOM Killer и убивает кого попало. И никак ты это не отследишь.
А вот этого у винды нету.
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
27.12.2018, 17:22
Цитата Сообщение от Azazel-San Посмотреть сообщение
А вот этого у винды нету.
Хм, а как тогда предлагается отрабатывать ситуацию "при попытке записи надо связать страницу виртуальной памяти с физической, но памяти нема"? Кто запросил связывание, того и по башке?
0
Mental handicap
 Аватар для Azazel-San
1246 / 624 / 171
Регистрация: 24.11.2015
Сообщений: 2,429
27.12.2018, 17:44
Renji, насколько я помню, если у нас не хватит физической памяти винда сделает файл подкачки (с n-ым размером) и будет пихать туда, затем если этого не хватит она просто начнет увеличивать размер файла подкачки (цифры могут быть поражающие психику), ну и когда и этот файл подкачки закончится (подойдет к какому-то лимиту, когда уже дальше некуда) у винды не остается ничего как экстренно кого-то убить.

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

зы Т.е. самой поддержки оверкомита у винды нету, но ВП разрешает выделять памятит больше чем есть физической.
0
2784 / 1937 / 570
Регистрация: 05.06.2014
Сообщений: 5,602
27.12.2018, 17:45
Цитата Сообщение от Azazel-San Посмотреть сообщение
Renji, насколько я помню, если у нас не хватит физической памяти винда сделает файл подкачки (с n-ым размером) и будет пихать туда, затем если этого не хватит она просто начнет увеличивать размер файла подкачки (цифры могут быть поражающие психику), ну и когда и этот файл подкачки закончится (подойдет к какому-то лимиту, когда уже дальше некуда) у винды не остается ничего как экстренно кого-то убить.
Я не про своп. Я про то что Линукс допускает код вида:
C++
1
2
3
    //сто тысяч раз по гигабайту!
    for(int i=0;i<100000;++i)
        new char[1<<30];
При том что ясное дело, ста терабайт свопа нигде не наблюдается.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
27.12.2018, 17:45
Помогаю со студенческими работами здесь

Освобождение и выделение динамической памяти
Привет народ, такой вопрос: Допустим в main, указателю выделил память, всё разумеется нормально, читаю пишу всё ок, потом передал этот...

Выделение и освобождение динамической памяти в функциях
Каким образом происходит выделение и очистка динамической памяти для массивов (в частности) в функциях? Написал код, но почему то ошибка...

Освобождение динамической памяти из-под прямоугольной матрицы
Добрый вечер! При попытке очищения памяти после завершения работы программы с прямоугольной матрицей m на n мне выдаёт ошибку а-ля...

Резервирование памяти/освобождение памяти для трехмерного массива
Необходимо создать трехмерный массив (A), в котором элементы вдоль направления Z выли бы выровнены по 16 байт. Есть две проблемы: ...

Освобождение памяти в C++
Добрый день! В моей программе в функции выделяется память (new char) под символьный массив, который является элементом структуры. Это...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу, и светлой Луне. В мире покоя нет и люди не могут жить в тишине. А жить им немного лет.
«Знание-Сила»
zbw 12.02.2026
«Знание-Сила» «Время-Деньги» «Деньги -Пуля»
SDL3 для Web (WebAssembly): Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 12.02.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами и вызывать обработчики событий столкновения. . . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL3_image
8Observer8 10.02.2026
Содержание блога Библиотека SDL3_image содержит инструменты для расширенной работы с изображениями. Пошагово создадим проект для загрузки изображения формата PNG с альфа-каналом (с прозрачным. . .
Установка Qt-версии Lazarus IDE в Debian Trixie Xfce
volvo 10.02.2026
В общем, достали меня глюки IDE Лазаруса, собранной с использованием набора виджетов Gtk2 (конкретно: если набирать текст в редакторе и вызвать подсказку через Ctrl+Space, то после закрытия окошка. . .
SDL3 для Web (WebAssembly): Работа со звуком через SDL3_mixer
8Observer8 08.02.2026
Содержание блога Пошагово создадим проект для загрузки звукового файла и воспроизведения звука с помощью библиотеки SDL3_mixer. Звук будет воспроизводиться по клику мышки по холсту на Desktop и по. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru