Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
САОД_ответы_catsto_NEW.doc
Скачиваний:
17
Добавлен:
16.04.2019
Размер:
365.57 Кб
Скачать

Int Year;

Int Month;

int Day

};

Это мы объявили тип данныхдату. Точка с запятой после закрывающей фигурной скобки обязательно нужна. Дело в том, что синтаксис объявлений позволяет одновременно задать и переменные нового типа. Точка с запятой указывает, что объявление закончено.

Еще пример:

struct Person {

Date BirthDay;

char szName [32];

};

Это мы объявили еще один тип Person с днем рождения (дату определили раньше) и именем – массив символов.

Теперь в программе можно объявлять переменные этих типов.

Person John = {{1986, 12, 23}, “John McCoy”};

Объявляет переменную John и инициализирует ее датой 23 декабря 1986 года и именем “John McCoy”.

Теперь эту информацию можно вывести на консоль:

cout << Jonh.szName << ” (”<<Jonh.BirthDay.Year<< ”-” << Jonh.BirthDay.Month << ”-” << Jonh.BirthDay.Day << ”)\n”;

К полям структуры добираются через точку.

Создадим динамическую структуру типа Person.

Person *p = new Person;

Теперь у нас есть только указатель на такую структуру. Этот указатель показывает на блок памяти, необходимый для размещения структуры, но данных там нет.

Вот как положить данный в структуру по указателю:

p -> BirthDate.Year = 1986;

p -> BirthDate.Month = 12;

p -> BirthDate.Day = 23;

char *t = p -> szName, *s = ”John McCoy”;

while (*t++ = *s++);

Обратите внимание, что мы используем ->, что бы добраться до полей структуры по указателю p, но до полей BirthDate добираемся через точку - p -> BirthDate это уже не указатель, а структура типа Date.

  1. Создание и уничтожение объектов в c#. Интерфейс iDisposable и освобождение ресурсов.

Создание объектов в C#: (

Статическое: int a; string s; …

Динамическое: int a[] = new int[5]; При помощи слова new )

Конструктор – неотъемлемый компонент класса. Нет классов без конструкторов. Конструктор представляет собой специальный метод класса, позволяющий создавать объекты класса. Одна из синтаксических особенностей этого метода в том, что его имя должно совпадать с именем класса. Если программист не определяет конструктор класса, то к классу автоматически добавляется конструктор по умолчанию - конструктор без аргументов. Заметьте, что если программист сам создает один или несколько конструкторов, то автоматического добавления конструктора без аргументов не происходит. Создание объектов чаще всего происходит при объявлении сущности в момент ее инициализации.

Рассмотрим создание трех объектов класса Person:

Person pers1 = new Person(), pers2 = new Person();

Person pers3= new Person("Петрова");

Сущности pers1, pers2 и pers3 класса Person объявляются с инициализацией, задаваемой унарной операцией new, которой в качестве аргумента передается конструктор класса Person. У класса может быть несколько конструкторов - это типичная практика, - отличающихся сигнатурой. В данном примере в первой строке вызывается конструктор без аргументов, во второй строке для сущности pers3 вызывается конструктор с одним аргументом типа string. Разберем в деталях процесс создания:

  • первым делом для сущности pers создается ссылка, пока висячая, со значением null;

  • затем в динамической памяти создается объект - структура данных с полями, определяемыми классом Person. Поля объекта инициализируются значениями по умолчанию: ссылочные поля - значением null, арифметические - нулями, строковые - пустой строкой. Эту работу выполняет конструктор по умолчанию, который, можно считать, всегда вызывается в начале процесса создания. Заметьте, если инициализируется переменная значимого типа, то все происходит аналогичным образом, за исключением того, что объект создается в стеке;

  • если поля класса проинициализированы, как в нашем примере, то выполняется инициализация полей заданными значениями;

  • если вызван конструктор с аргументами, то начинает выполняться тело этого конструктора. Как правило, при этом происходит инициализация отдельных полей класса значениями, переданными конструктору. Так, поле fam объекта pers3 получает значение "Петрова";

  • На заключительном этапе ссылка связывается с созданным объектом.

Процесс создания объектов становится сложнее, когда речь идет об объектах, являющихся потомками некоторого класса. В этом случае, прежде чем создать сам объект, нужно вызвать конструктор, создающий родительский объект. Зачем классу нужно несколько конструкторов? Дело в том, что, в зависимости от контекста и создаваемого объекта, может требоваться различная инициализация его полей. Перегрузка конструкторов и обеспечивает решение этой задачи.

Конструктор может быть объявлен с атрибутом private. Понятно, что в этом случае внешний пользователь не может воспользоваться им для создания объектов. Но это могут делать методы класса, создавая объекты для собственных нужд со специальной инициализацией.

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

Деструктор и метод Finalize

Если задача создания объектов полностью возлагается на программиста, то задача удаления объектов, после того, как они стали не нужными, в Visual Studio .Net снята с программиста и возложена на соответствующий инструментарий - сборщик мусора. В классическом варианте языка C++ деструктор так же необходим классу, как и конструктор. В языке C# y класса может быть деструктор, но он не занимается удалением объектов и не вызывается нормальным образом в ходе выполнения программы. Так же, как и статический конструктор, деструктор класса, если он есть, вызывается автоматически в процессе сборки мусора. Его роль - в освобождении ресурсов, например, файлов, открытых объектом. Деструктор C# фактически является финализатором (finalizer)

Проблема освобождения неуправляемых ресурсов в .NET решается за счет применения метода Finalize, который наследуется любым классом от object. По умолчанию метод Object.Finalize

void Finalize()... не делает ничего. Если же требуется, чтобы сборщик мусора при уничтожении объекта выполнил завершающие операции (закрыл ранее открытый файл, разорвал соединение с базой данных), то в соответствующем классе этот метод должен быть переопределен. В нем должен быть размещен соответствующий программный код, обеспечивающий освобождение ресурсов.

В .NET метод Finalize занимает особое место, и его использование регламентируется следующими ограничениями:

* предполагается, что управление этому методу передается непосредственно сборщиком мусора при удалении данного объекта;

* НЕ РЕКОМЕНДУЕТСЯ вызывать метод Finalize из методов, непосредственно не связанных с уничтожением объекта, тем более из методов других классов. Поэтому метод Finalize должен объявляться со спецификатором protected. При этом единственным корректным вызовом метода считается вызов метода Finalize для базового класса с использованием нотации base (...base.Finalize();...);

* в методе Finalize должны освобождаться только те ресурсы, которыми владеет уничтожаемый объект.

Транслятор C# знает об особом статусе метода Finalize и о проблемах, связанных с его использованием. Соответствующее объявление метода в классе сопровождается предупреждением (warning), которое гласит:

Introgucing a 'Finalize' method can interfere with destructor invocation.

Did you intend to declare a destructor?

То есть, не лучше ли заменить этот метод деструктором? Дело в том, что для C# транслятора следующие строки кода являются эквивалентными:

~MyClass() // объявление деструктора.

{ // Здесь реализуются алгоритмы освобождения ресурсов.}

protected override void Finalize() // Объявление метода финализации.

{ try

{ // Здесь реализуются алгоритмы освобождения ресурсов. }

finally

{ base.Finalize(); // Вызов Finalize для базового класса. }

}

и если в классе объявляется деструктор (деструктор всегда предпочтительнее), то присутствие метода protected override void Finalize()... в том же классе воспринимается как ошибка.

Таким образом, освобождение ресурсов в программе на C# возлагается на Finalize, точнее, на деструктор, который запускается непосредственно GC.

Совмещение уничтожения объекта с освобождением ресурсов не может рассматриваться как оптимальное решение. Время начала работы сборщика мусора остается неопределенным даже после его активизации, а немедленное освобождение ресурса может оказаться решающим для дальнейшего выполнения приложения.

*В структурах определение деструкторов невозможно. Они применяются только в классах.

*Класс может иметь только один деструктор.

*Деструкторы не могут наследоваться или перегружаться.

*Деструкторы невозможно вызвать. Они запускаются автоматически.

*Деструктор не принимает модификаторы и не имеет параметров.

Finalize вызывается рекурсивно для всех экземпляров цепочки наследования начиная с самого дальнего и заканчивая самым первым.

Пустые деструкторы использовать не следует. Если класс содержит деструктор, то в очереди метода Finalize создается запись. При вызове деструктора вызывается сборщик мусора, выполняющий обработку очереди. Если деструктор пустой, это приводит только к ненужному снижению производительности.

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

Существует возможность принудительно выполнить сборку мусора, вызвав метод Collect, но в большинстве случаев этого следует избегать, потому что это может привести с проблемам с производительностью.

Использование деструкторов для освобождения ресурсов.

В целом, язык C# не требует управления памятью в той степени, в какой это требуется в случае разработки кода на языке, не рассчитанном на среду выполнения со сборкой мусора. Это связано с тем, что сборщик мусора платформы .NET Framework неявным образом управляет выделением и высвобождением памяти для объектов. Однако при инкапсуляции приложением неуправляемых ресурсов, например окон, файлов и сетевых подключений, для высвобождения этих ресурсов следует использовать деструкторы. Если объект требует уничтожения, то сборщик мусора запускает выполнение метода Finalize этого объекта.

Высвобождение ресурсов явным образом

В случае, когда приложением используется ценный внешний ресурс, также рекомендуется обеспечить способ высвобождения этого ресурса явным образом, прежде чем сборщик мусора освободит этот объект. Это выполняется путем реализации метода Dispose интерфейса IDisposable, который выполняет необходимую очистку для объекта. Это может значительно повысить производительность приложения. Даже в случае использования данного явного управления ресурсами деструктор становится резервным средством очистки ресурсов, если вызов метода Dispose не будет выполнен.

IDisposable – интерфейс определяет методы высвобождения распределенных ресурсов.

Язык C#

[ComVisibleAttribute(true)]

public interface IDisposable

Язык Visual C++

[ComVisibleAttribute(true)]

public interface class IDisposable

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

Метод Dispose этого интерфейса используется вместе со сборщиком мусора для высвобождения неуправляемых ресурсов явным образом. Пользователь объекта может вызвать этот метод, когда объект ему больше не нужен.

Программистам языка C++ следует ознакомиться с документом Destructors and Finalizers in Visual C++. В версии платформы .NET Framework 2.0 компилятор C++ обеспечивает поддержку реализации детерминированной утилизации ресурсов и не позволяет реализовать метод Dispose непосредственно.

Добавление интерфейса IDisposable приводит к изменению версию, так как при этом меняется семантика класса.

Вызов IDisposable Interface

При вызове класса, реализующего интерфейс IDisposable, следует использовать структуру try/finally, гарантирующую утилизацию неуправляемых ресурсов даже в случае прерывания выполнения приложения в результате исключения.

Обратите внимание, что вместо структуры try/finally можно использовать оператор using.

Как правило, при использовании объекта IDisposable его следует объявить и создать в операторе using. Оператор using соответствующим образом вызывает метод Dispose в объекте и (если он используется как описано выше) приводит к выводу объекта из области действия сразу после вызова Dispose. В рамках блока using объект доступен только для чтения и не может быть изменен или переназначен.

Оператор using гарантирует вызов метода Dispose, даже если при вызове методов в объекте происходит исключение. Такого же результата можно достичь при размещении объекта в блоке try и последующем вызове метода Dispose в блоке finally; фактически, это способ преобразования оператора using компилятором. Приведенный ранее пример кода во время компиляции расширяется до следующего кода (при создании ограниченной области действия для объекта обратите внимание на дополнительные фигурные скобки).

  1. Особенности наследования в C++ и C#. Роль наследования в программировании.

Насле́дование — один из четырёх важнейших механизмов объектно-ориентированного программирования (наряду с инкапсуляцией, полиморфизмом и абстракцией), позволяющий описать новый класс на основе уже существующего (родительского), при этом свойства и функциональность родительского класса наследуются новым классом.

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

В С# есть только прямое наследование и нет множественного. В С++ множественное есть.

Типы наследования

Простое наследование

Класс, от которого произошло наследование, называется базовым или родительским (англ. base class). Классы, которые произошли от базового, называются потомками, наследниками или производными классами (англ. derived class).

В некоторых языках используются абстрактные классы. Абстрактный классэто класс, содержащий хотя бы один абстрактный метод, он описан в программе, имеет поля, методы и не может использоваться для непосредственного создания объекта. То есть от абстрактного класса можно только наследовать. Объекты создаются только на основе производных классов, наследованных от абстрактного. Например, абстрактным классом может быть базовый класс «сотрудник ВУЗа», от которого наследуются классы «аспирант», «профессор» и т. д. Так как производные классы имеют общие поля и функции (например, поле «год рождения»), то эти члены класса могут быть описаны в базовом классе. В программе создаются объекты на основе классов «аспирант», «профессор», но нет смысла создавать объект на основе класса «сотрудник вуза».

Множественное наследование

При множественном наследовании у класса может быть более одного предка. В этом случае класс наследует методы всех предков. Достоинства такого подхода в большей гибкости. Множественное наследование реализовано в C++. Из других языков, предоставляющих эту возможность, можно отметить Python. Множественное наследование поддерживается в языке UML.

Множественное наследование — потенциальный источник ошибок, которые могут возникнуть из-за наличия одинаковых имен методов в предках. В языках, которые позиционируются как наследники C++ (Java, C# и др.), от множественного наследования было решено отказаться в пользу интерфейсов. Практически всегда можно обойтись без использования данного механизма. Однако, если такая необходимость все-таки возникла, то, для разрешения конфликтов использования наследованных методов с одинаковыми именами, возможно, например, применить операцию расширения видимости — «::» — для вызова конкретного метода конкретного родителя.

Большинство современных объектно-ориентированных языков программирования (C#, Java, Delphi и др.) поддерживает возможность одновременно наследоваться от класса-предка и реализовать методы нескольких интерфейсов одним классом. Этот механизм позволяет во многом заменить множественное наследование — методы интерфейсов необходимо переопределять явно, что исключает ошибки при наследовании функциональности одинаковых методов различных классов-предков.

Наследование в языке C++

class A{ //базовый класс};

class B : public A{ //public наследование}

class C : protected A{ //protected наследование}

class Z : private A{ //private наследование}

В C++ существует три типа наследования: public, protected, private. Спецификаторы доступа членов базового класса меняются в потомках следующим образом:

* при public-наследовании все спецификаторы остаются без изменения.

* при protected-наследовании все спецификаторы остаются без изменения, кроме спецификатора public, который меняется на спецификатор protected (то есть public-члены базового класса в потомках становятся protected).

* при private-наследовании все спецификаторы меняются на private.

Одним из основных преимуществ public-наследования является то, что указатель на классы—наследники может быть неявно преобразован в указатель на базовый класс, то есть для примера выше можно написать:

A* a = new B;

1. Наследование представляет собой способность производить новый класс из существующего базового класса.

2. Производный класс — это новый класс, а базовый класс — существующий класс.

3. Когда вы порождаете один класс из другого (базового класса), производный класс наследует элементы базового класса.

4. Для порождения класса из базового начинайте определение производного класса ключевым словом class, за которым следует имя класса, двоеточие и имя базового класса, например class dalmatian: dog.

5. Когда вы порождаете класс из базового класса, производный класс может обращаться к общим элементам базового класса, как будто эти элементы определены внутри самого производного класса. Для доступа к частным данным базового класса производный класс должен использовать интерфейсные функции базового класса.

6. Внутри конструктора производного класса ваша программа должна вызвать конструктор базового класса, указывая двоеточие, имя конструктора базового класса и соответствующие параметры сразу же после заголовка конструктора производного класса.

7. Чтобы обеспечить производным классам прямой доступ к определенным элементам базового класса, в то же время защищая эти элементы от оставшейся части программы, C++ обеспечивает защищенные {protected) элементы класса. Производный класс может обращаться к защищенным элементам базового класса, как будто они являются общими. Однако для оставшейся части программы защищенные элементы эквивалентны частным.

8. Если в производном и базовом классе есть элементы с одинаковым именем, то внутри функций производного класса C++ будет использовать элементы производного класса. Если функциям производного класса необходимо обратиться к элементу базового класса, вы должны использовать оператор глобального разрешения, например base class:: member.

  1. Переопределение операций в C++ и C#. Inline методы и эффективность программ. Назначение метода GetHashCode в .Net.

Перегрузка операторов напоминает перегрузку функций, перегрузка операторов является фактически одним из видов перегрузки функций. Вводятся дополнительные правила оператор перегружается пользователем относительно определенного пользователем типа данных, такого как класс.

Когда оператор перегружается, то ничего из его исходного значения не теряется. Наоборот, он приобретает дополнительное значение, связанное с классом, для которого оператор был определен.

Для перегрузки оператора создается оператор-функция (operator function). Чаще всего, оператор-функция является членом класса или дружественной классу, для которого она определена.

Оператор-функция

Возвращаемый_тип имя_класса::operator#(список_аргументов)

{Выполняемая операция}

Часто типом возвращаемого значения оператор-функция является класс для которого она определена. Вместо # ставится перегружаемый оператор, например:+. Operator+. Содержание списка список_аргрументов зависит от реализации оператор-функции и от типа перегружаемого оператора.

Нельзя менять приоритет операторов,нельзя менять число операндов оператора. Например нельзя перегрузить \, так чтобы использовался 1 операнд. Операторы которые нельзя перегружать: ‘.’ , ‘::’ , ‘.*’ ,’ ?’, а также операторы препроцессора.

В C++ мы можем определять операции для объектов нашего класса.

class CDate {