Главная страница
Навигация по странице:

  • 14.9.3 Пример применение прямого доступа для перемещения по файлу

  • 15 Графический интерфейс С++/CLI 15.1 Введение в С++/CLI C++/CLI

  • 15.1.1 Управляемые типы, массивы и указатели

  • Маршалингом (marshalling

  • Из типа В тип Метод Заголовочный файл Строки в языке C

  • Из типа В тип Метод Заголовочный файл

  • Стандартная библиотека C++

  • 15.1.3 Указатели С++ и ссылки CLI

  • ООП. Пособие по ООП в С++. Создание сложных программных систем Объектно ориентированная технология


    Скачать 2.1 Mb.
    НазваниеСоздание сложных программных систем Объектно ориентированная технология
    Дата21.02.2022
    Размер2.1 Mb.
    Формат файлаpdf
    Имя файлаПособие по ООП в С++.pdf
    ТипАнализ
    #369034
    страница14 из 15
    1   ...   7   8   9   10   11   12   13   14   15

    seekg() перемещает ассоциированный с файлом указатель чтения на offset записей или символов. Функция seekg() объявлена в istream .
    И istream и ostream наследуются классом fstream, который позволяет выполнять операции ввода и вывода.
    Функция seekp() перемещает ассоциированный с файлом указатель записи на offset записей или символов. Функция seekp() определена в ostream и наследована ostream.
    //изменение записи с заданным номером в двустороннем потоке void
    ChengeRecord(
    int pos,
    char
    *namefile)
    { fstream fdirect(namefile,ios::binary|ios::out| ios::in);
    record r; fdirect.seekg(pos*
    sizeof
    (r),ios::beg); fdirect.read((
    char
    *) &r, sizeof r); fdirect.seekg(- sizeof
    (r),ios::cur); strcpy(r.info,
    "****"
    ); fdirect.write((
    char
    *) &r, sizeof r);
    }
    14.9.3 Пример применение прямого доступа для перемещения по файлу
    1) Создать поток для прямого доступа к двоичному файлу, содержащему три записи с данными
    Шилдт Методики 2015
    Страуструп Язык С++ 2015
    Иванова Г.И. ООП 2012
    1) Открыть поток для чтения и записи
    2) Изменить фамилию Страуструп на Straustrup. Над найти запись с фамилией Страуструп. Она будет в процессе поиска прочитана в переменную Х, после ее чтения указатель за этой записью.
    3) Изменить прочитанную запись в переменной Х.
    4) Записать измененную запись на ее место в файле, для этого:
    • Переместить указатель на одну запись назад
    • Выполнить запись в файл значения переменной Х.
    5) Показать содержание файла. Для этого надо установить указатель перед первой записью
    #include
    "stdafx.h"
    #include

    #include

    #include

    #include
    using namespace std; struct book
    { string Fam; string Name; int year;
    }; void out_bin_file(fstream &fb)
    {
    book x; fb.read((
    char
    *)&x,
    sizeof
    (book)); while
    (!fb.eof())
    { cout<char
    *)&x,
    sizeof
    (book));
    } fb.close();
    }
    //поиск записи в файле и изменения void cheng_bin_file(fstream &fbpr, string newfnam, string oldfnam)
    { book x; int i=0; do
    { fbpr.read((
    char
    *)&x,
    sizeof
    (book)); if
    (x.Fam==oldfnam)
    {
    //заменитть фамилию в записи на новую x.Fam=newfnam;
    //смещение назад на одну запись (на старое место) fbpr.seekg(
    sizeof
    (book)*(i-1),ios::cur);
    //запись Х в файл fbpr.write((
    char
    *)&x, sizeof
    (book)); return
    ;
    }
    }
    while
    (!fbpr.eof());
    } int
    _tmain(
    int argc, _TCHAR* argv[])
    {
    setlocale(LC_ALL,
    "rus"
    ); char fnameBin[30]=
    "B.dat"
    ; book x; fstream fbpr; fbpr.open(fnameBin,ios::out|ios::in|ios::binary
    ); if
    !fbpr)
    { cout<<
    "файл не открыт"
    ; return
    1;
    } string oldfnam=
    "Страуструп"
    , newfnam=
    "Straustrup"
    ; cheng_bin_file(fbpr, newfnam, oldfnam);
    //установить указатель перед первой записью
    //чтобы вывести записи файла и не открывать его снова fbpr.seekg(
    sizeof
    (book)*0,ios::beg); out_bin_file(fbpr); fbpr.close(); return
    0;
    }
    15 Графический интерфейс С++/CLI
    15.1
    Введение в С++/CLI
    C++/CLI — язык для среды программирования Microsoft .NET. Он интегрирует С++ стандарта ISO с Объединённой Системой Типов (Unified
    Type
    System,
    UTS), рассматриваемой как часть общеязыковой инфраструктуры (Common Language Infrastructure, CLI). Он поддерживает и исходный уровень, и функциональную совместимость исполняемых файлов, скомпилированных с родного и управляемого C++.

    C++/CLI представляет собой дальнейшее развитие Managed C++.
    C++/CLI стандартизирован в ECMA как ECMA-372.
    При реализации обработчиков событий элементов формы может возникнуть необходимость писать код на языке C++/CLI, но новых конструкций будет немного, они легко ассоциируются со стандартом С++.
    Операторы ^ и gcnew языка C++ CLI - это местные аналоги *(указатель) и new из обычного C++, имеющие место в managed C++, он же C++/CLI.
    Последний представляет собой сочетание нормального C++ и C#.
    C++/CLI - это набор расширений языка C++, позволяющий создавать гибридные управляемые и низкоуровневые библиотеки DLL.
    С применением расширений C++/CLI вы сможете определять управляемые и неуправляемые классы и функции в пределах одного файла
    .cpp, использовать управляемые и низкоуровневые типы C и C++, как в обычном программном коде на C++, то есть простым подключением заголовочного файла и связыванием с библиотекой.
    Эти широчайшие возможности можно использовать для создания управляемых типов-оберток, пригодных для использования в любом языке
    .NET, а так же низкоуровневые классы-обертки и функции (доступные через файлы .dll, .lib и .h), пригодные для использования в программном коде на
    C/C++.
    “Что же такое MC++?”. С одной стороны, вы можете смело использовать в своих программах все привычные возможности C++, шаблоны и множественное наследование, перегрузку операторов и прямую работу с памятью. Вся разница лишь в том, что компилятор будет генерировать управляемый код (MSIL) вместо ассемблера. Но! Это касается только обычных типов C++. Если же у вас возникнет необходимость (а она обязательно возникнет) в использовании gc- и value-типов, то вам не придётся заботиться об удалении объектов, CLR будет сама производить начальную инициализацию переменных и проверять допустимость значений аргументов во время исполнения. Платой за это будет следование всем ограничениям управляемой среды. Таким образом, фактически мы имеем два разных языка в одном, которые можно легко смешивать. Единственная проблема – теперь нам придётся постоянно помнить, с каким из них в данный момент мы имеем дело.
    Что такое “управляемый” и “неуправляемый” код? С неуправляемым всё ясно – это обычный “родной” код Windows/Intel. С управляемыми объектами тоже понятно – CLR может их полностью контролировать. Не понятно только, как быть с обычными C++ программами, которые не используют управляемые объекты, но компилируются в MSIL код.

    15.1.1 Управляемые типы, массивы и указатели
    CLR поддерживает автоматическую сборку мусора и обеспечивает безопасную передачу данных между различными частями системы. Для обеспечения этих возможностей среда CLR должна владеть полной информацией о типах данных.
    В результате получена возможность создавать и использовать такие типы наряду с обычными типами C++, не прибегая к ухищрениям наподобие библиотек типов для COM-объектов.
    Объявление управляемого типа в Managed Extensions for C++ (CLR), производится с помощью ключевых слов __gc или __value.
    __gc
    Идентификатор __gc применяется для объявления сложных типов, массивов и указателей, размещаемых в куче среды исполнения CLR.
    Сокращение gc, скорее всего, происходит от garbage collection. Рассмотрим пример:
    __gc class Foo { public:

    Foo(); void Fun();
    }; void test()
    {
    Foo *p = new Foo; p->Fun();
    }
    Ключевое слово __gc перед объявлением класса говорит компилятору, что наш класс является управляемым и подчиняется всем правилам среды
    CLR. В частности, нам не нужно вызывать деструктор для удаления объекта из памяти, эту работу за нас сделает CLR. Более того, если мы всё же вызовем оператор delete, то компилятор выдаст сообщение об ошибке, говорящее о том, что в нашем классе не определён деструктор. Определение деструктора может быть добавлено, и вызов оператора delete приведёт к его немедленному вызову, но, тем не менее, память, занятая объектом, освобождена не будет.
    Если же оператор delete не вызывается, то деструктор будет вызван CLR по своему усмотрению во время сборки мусора. Такое поведение деструкторов управляемых классов обусловлено тем, что компилятор фактически переименовывает их в метод Finalize, являющийся стандартным для среды

    CLR и вызываемый ей непосредственно перед удалением объекта из памяти.
    Здесь будет уместно заметить, что все управляемые типы CLR происходят от класса System::Object, который и содержит виртуальный метод Finalize.
    Компилятор добавляет это наследование автоматически, хотя вполне допустимо делать это явно.
    Ещё одной особенностью приведённого выше кода является то, что мы можем создать экземпляр класса только в управляемой куче, например, следующий код является некорректным и приведёт к ошибке компиляции: void test()
    {
    Foo p; // ошибка p.Fun();
    }
    __gc arrays
    Так же, как и для объектов управляемых типов, память для управляемых массивов выделяется в куче CLR. Массивы могут содержать переменное число элементов и всегда инициализируются при создании. Для объявления управляемых массивов используется специальный синтаксис: int ar __gc[] = new int __gc[10]; ar[0] = 1; ar = new int __gc[20];
    Создать массив объектов Foo можно следующим образом
    Foo *ar[] = new Foo*[10]; ar[0] = new Foo;
    При использовании расширения C++/CLI маршалинг выполняется вручную, благодаря чему разработчик имеет более полный контроль и более полное представление о накладных расходах.
    Маршалингом (marshalling) называется процесс создания моста между управляемым кодом (managed code) и неуправляемым кодом (unmanaged code)
    это подсистема, которая передает сообщения из managed-окружения в unmanaged-окружение и обратно.
    Еще одно преимущество расширения C++/CLI состоит в том, что оно позволяет имитировать интерфейс объединения запросов, даже если у вас нет доступа к исходным текстам вызывающего кода, многократно вызывая низкоуровневые методы без необходимости каждый раз пересекать границу между управляемым и неуправляемым кодом.

    В коде, представленном ниже, мы реализовали неуправляемый класс
    NativeEmployee и управляемый класс Employee, служащий оберткой для первого. Управляемый код будет обращаться только к управляемому классу.
    #include
    #include
    #include
    #include using namespace System; using namespace System::Runtime::InteropServices; class NativeEmployee { public:
    NativeEmployee(const wchar_t *employeeName, int age)
    : _employeeName(employeeName), _employeeAge(age) { } void DoWork(const wchar_t **tasks, int numTasks) { for (int i = 0; i < numTasks; i++) { wprintf(L"Пользователь %s работает в задаче %s\n",
    _employeeName.c_str(), tasks[i]);
    }
    } int GetAge() const { return _employeeAge;
    } const wchar_t *GetName() const { return _employeeName.c_str();
    } private: std::wstring _employeeName; int _employeeAge;
    };
    #pragma managed namespace EmployeeLib { public ref class Employee {
    { public:
    Employee(String ^employeeName, int age) {
    // Вариант 1:
    // IntPtr pEmployeeName =
    Marshal::StringToHGlobalUni(employeeName);
    // m_pEmployee = new NativeEmployee(
    // reinterpret_cast(pEmployeeName.ToPointer()), age);
    // Marshal::FreeHGlobal(pEmployeeName);
    // Вариант 2 (прямой указатель на закрепленную
    // управляемую строку, самый быстрый): pin_ptr ppEmployeeName =
    PtrToStringChars(employeeName);
    _employee = new NativeEmployee(ppEmployeeName, age);
    }
    Employee() { delete _employee;
    _employee = nullptr;

    } int GetAge() { return _employee->GetAge();
    }
    String ^GetName() {
    // Вариант 1:
    // return Marshal::PtrToStringUni(
    // (IntPtr)(void *) _employee->GetName());
    // Вариант 2: return msclr::interop::marshal_as(_employee-
    >GetName());
    // Вариант 3 (самый быстрый): return gcnew String(_employee->GetName());
    } void DoWork(array^ tasks) {
    // marshal_context - это управляемый класс, размещаемый
    // (в динамической памяти сборщика мусора) с использованием
    // семантики, напоминающей стек. Его деструктор
    IDisposable::Dispose()
    // будет вызван после выхода из области видимости этой функции msclr::interop::marshal_context ctx; const wchar_t **pTasks = new const wchar_t*[tasks->Length]; for (int i = 0; i < tasks->Length; i++) {
    String ^t = tasks[i]; pTasks[i] = ctx.marshal_as(t);
    } m_pEmployee->DoWork(pTasks, tasks->Length);
    // деструктор контекста освободит неуправляемую
    // память, выделенную методом marshal_as delete[] pTasks;
    } private:
    NativeEmployee *_employee;
    };
    }
    В этом коде конструктор Employee демонстрирует два способа преобразования управляемых строк в неуправляемый: первый основан на выделении памяти с помощью GlobalAlloc, которую необходимо будет освободить явно, а второй временно закрепляет управляемую строку в памяти и возвращает прямой указатель. Второй способ выполняется быстрее, но его можно использовать, только когда неуправляемый код принимает строки, завершающиеся нулевым символом, в кодировке UTF-16, и он не записывает ничего в память по указателю. Кроме того, закрепление управляемых объектов на длительное время может привести к фрагментации памяти, поэтому, если перечисленные требования не удовлетворяются, вам придется прибегнуть к копированию строк.

    Метод GetName() класса Employee демонстрирует три способа преобразования неуправляемых строк в управляемые: первый основан на использовании класса
    System.Runtime.InteropServices.Marshal, второй использует функцию marshal_as, объявленную в заголовочном файле msclr/marshal.h, и, наконец, третий использует конструктор класса
    System.String, являющийся наиболее быстрым.
    Метод DoWork() класса Employee принимает управляемый массив (или управляемые строки) и преобразует его в массив указателей типа wchar_t, указывающих на строки; фактически это массив строк в стиле языка C.
    Преобразование управляемых строк в неуправляемые выполняется с применением метода marshal_as класса объекта marshal_context. В отличие от глобальной функции marshal_as, объект marshal_context используется для преобразований, требующих освобождения занимаемых при этом ресурсов.
    Обычно для преобразования управляемых данных в неуправляемые метод marshal_as выделяет неуправляемую память, которую следует освободить после выполнения операции. Объект marshal_context содержит связанный список операций освобождения ресурсов, которые выполняются в момент уничтожения объекта.
    Подводя итоги можно сказать, что расширение C++/CLI обеспечивает полный контроль над маршалингом и не требует дублирования объявлений функций, что чревато ошибками, особенно когда часто приходится изменять сигнатуры неуправляемых функций.
    15.1.2 Тип String^
    Для использования методов marshal_as() и marshal_context::marshal_as() нужно подключить один или несколько заголовочных файлов из папки msclr: marshal.h, marshal_windows.h, marshal_cppstd.h или marshal_atl.h.
    Определить, какой метод использовать и какой заголовочный файл подключать, можно из таблицы:
    Из типа
    В тип
    Метод
    Заголовочный
    файл
    Строки в языке C
    System::String^ const char* marshal_context::marshal_as(
    ) marshal.h const char*
    System::String^ marshal_as() marshal.h char*
    System::String^ marshal_as() marshal.h

    Из типа
    В тип
    Метод
    Заголовочный
    файл
    System::String^ const wchar_t* marshal_context::marshal_as(
    ) marshal.h const wchar_t*
    System::String^ marshal_as() marshal.h wchar_t*
    System::String^ marshal_as() marshal.h
    WinAPI и COM
    System::IntPtr
    HANDLE marshal_as() marshal_windows.
    h
    HANDLE
    System::IntPtr marshal_as() marshal_windows.
    h
    System::String^
    BSTR marshal_context::marshal_as(
    ) marshal_windows.
    h
    BSTR
    System::String^ marshal_as() marshal.h
    System::String^ bstr_t marshal_as() marshal_windows.
    h bstr_t
    System::String^ marshal_as() marshal_windows.
    h
    Стандартная библиотека C++
    System::String^ std::string marshal_as() marshal_cppstd.h std::string
    System::String^ marshal_as() marshal_cppstd.h
    System::String^ std::wstring marshal_as() marshal_cppstd.h std::wstring
    System::String^ marshal_as() marshal_cppstd.h
    ATL
    System::String^
    CStringT marshal_as() marshal_atl.h
    CStringT
    System::String^ marshal_as() marshal_atl.h
    System::String^
    CStringT> marshal_as() marshal_atl.h
    CStringT>
    System::String^ marshal_as() marshal_atl.h
    System::String^
    CComBSTR marshal_as() marshal_atl.h
    CComBSTR
    System::String^ marshal_as() marshal_atl.h

    Другие преобразования, кроме указанных в таблице, не поддерживаются.
    15.1.3 Указатели С++ и ссылки CLI
    В Visual C++ (CLR – Common Language Runtime) поддерживаются три типа указателей:
    • управляемые указатели (managed pointers);
    • неуправляемые указатели (unmanaged pointers);
    • неуправляемые указатели на функции (unmanaged function pointers).
    Отличие между управляемыми и неуправляемыми указателями состоит в следующем: в отличие от управляемого указателя неуправляемому указателю можно присвоить любой адрес памяти, даже той, которая находится за пределами исполнительной среды. В этом случае память явяется
    нерегулируемой.
    В случае регулируемой памяти управляемому указателю присвоить адрес за пределами исполнительной среды не удастся.
    Управляемые указатели – это указатели ссылочного типа. Эти указатели передаются для аргументов, методов, которые передаются по ссылке. Эти указатели являются совместимыми со спецификацией Common
    Language Runtime (CLR).
    Управляемые указатели являются ссылками на объекты. Эти объекты размещаются в общей управляемой памяти, которая выделяется для программы в момент ее выполнения.
    В таких указателях вместо символа ‘*’ применяется символ ‘^’. Для выделения
    В управляемых указателях утилита gcnew формирует экземпляр некоторого объекта. Утилита gcnew выделяет память для экземпляра объекта и возвращает ссылку на этот объект.
    Описание этого объекта должно начинаться с ключевого слова ref.
    Ключевое слово ref дает информацию о том, что описание есть ссылочного типа. Общая форма использования ключевого слова ref имеет вид:
    ref описание;
    После такого описания память под объект выделяется с помощью утилиты gcnew.
    Пример использования управляемого указателя на функцию в среде C++/CLI
    Использование указателя на функцию в среде C++/CLI для приложений, созданных по шаблону Windows Forms. В этом случае нужно использовать так называемые делегаты. Делегаты поддерживаются в .NET Framework.

    Пусть даны 2 функции, которые имеют одинаковое количество параметров и возвращают одинаковое значение:
    // функция, которая возводит x в степень y public: double Power(int x, int y)
    { double res; res = System::Math::Pow((double)x, (double)y); return res;
    }
    // функция, которая делит x на y public: double Div(int x, int y)
    { double res; res = (double)x / (double)y; return res;
    }
    Чтобы использовать делегат нужно сначала в некотором месте класса формы (или другого класса) дать следующее описание:
    // описание делегата DelFun, который получает два параметра типа int
    // и возвращает значение типа double public: delegate double DelFun(int , int);
    После этого, в некотором методе (например, обработчике события клика на кнопке) можно использовать этот указатель следующим образом: double result;
    DelFun ^ PFun = gcnew DelFun(this, &Lab1::Form1::Power); result = PFun(3,2); // result = 9
    PFun = gcnew DelFun(this, &Lab1::Form1::Div); result = PFun(3,2); // result = 1.5
    В вышеприведенном примере в строках
    &Lab1::Form1::Power
    &Lab1::Form1::Div идентификаторы имеют следующее назначение:
    Lab1 – название проекта;
    Form1 – название главной формы приложения (имя переменной);
    Power – название функции.
    1   ...   7   8   9   10   11   12   13   14   15


    написать администратору сайта