Статический и динамический полиморфизм — КиберПедия 

Семя – орган полового размножения и расселения растений: наружи у семян имеется плотный покров – кожура...

Поперечные профили набережных и береговой полосы: На городских территориях берегоукрепление проектируют с учетом технических и экономических требований, но особое значение придают эстетическим...

Статический и динамический полиморфизм

2021-12-07 34
Статический и динамический полиморфизм 0.00 из 5.00 0 оценок
Заказать работу

Примеры

C++

class A { public: int a, b; //данные открытого интерфейса int ReturnSomething(); //метод открытого интерфейса private: int Aa, Ab; //скрытые данные void DoSomething(); //скрытый метод};

Класс А инкапсулирует свойства Aa, Ab и метод DoSomething, представляя внешний интерфейс ReturnSomething, a, b.

· Полиморфизм. При полиморфизме некоторые части (методы) родительского класса заменяются новыми, реализующими специфические для данного потомка действия. Таким образом, интерфейс классов остаётся прежним, а реализация методов с одинаковым названием и набором параметров различается. С полиморфизмом тесно связано позднее связывание.

Полиморфи́зм (от греч. πολὺ- — много, и μορφή — форма) в языках программирования — возможность объектов с одинаковой спецификацией иметь различную реализацию.

Язык программирования поддерживает полиморфизм, если классы с одинаковой спецификацией могут иметь различную реализацию — например, реализация класса может быть изменена в процессе наследования[1].

Кратко смысл полиморфизма можно выразить фразой: «Один интерфейс, множество реализаций».

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

Полиморфизм позволяет писать более абстрактные программы и повысить коэффициент повторного использования кода. Общие свойства объектов объединяются в систему, которую могут называть по-разному — интерфейс, класс. Общность имеет внешнее и внутреннее выражение:

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

Примеры

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

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

[править]В объектно-ориентированных языках

В объектно-ориентированных языках класс является абстрактным типом данных.[Прим. 1] Полиморфизм реализуется с помощьюнаследования классов и виртуальных функций. Класс-потомок наследует сигнатуры методов класса-родителя, а реализация, в результате переопределения метода, этих методов может быть другой, соответствующей специфике класса-потомка. Другие функции могут работать с объектом как с экземпляром класса-родителя, но если при этом объект на самом деле является экземпляром класса-потомка, то во время исполнения будет вызван метод, переопределенный в классе-потомке. Это называется поздним связыванием. [Примером использования может служить обработка массива, содержащего экземпляры как класса-родителя, так и класса-потомка: очевидно, что такой массив может быть объявлен только как массив типа класса-родителя и у объектов массива могут вызываться только методы этого класса, но если в классе-потомке какие-то методы были переопределены, то в режиме исполнения для экземпляров этого класса будут вызваны именно они, а не методы класса-родителя.]

Класс-потомок сам может быть родителем. Это позволяет строить сложные схемы наследования — древовидные илисетевидные.

Абстрактные (или чисто виртуальные) методы не имеют реализации вообще (на самом деле некоторые языки, например C++, допускают реализацию абстрактных методов в родительском классе). Они специально предназначены для наследования. Их реализация должна быть определена в классах-потомках.

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

Параметрический полиморфизм

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

[править]Специальный полиморфизм

Специальный (или лат. ad hoc) полиморфизм допускает специальную реализацию для данных каждого типа. Например, используемая в нашем примере функцией сортировки функция сравнения должна быть определена по-разному для чисел, кортежей, списков, т. е. она является специально полиморфной.[ источник не указан 949 дней ]

В Haskell есть деление на классы и экземпляры (instance), которого нет в ООП. Класс определяет набор и сигнатуры методов (возможно, задавая для некоторых или всех из них реализации по умолчанию), а экземпляры реализуют их. Таким образом, автоматически отпадает проблема множественного наследования. Классы не наследуют и не переопределяют методы других классов — каждый метод принадлежит только одному классу. Такой подход проще, чем сложная схема взаимоотношений классов в ООП. Некоторый тип данных может принадлежать нескольким классам; класс может требовать, чтобы каждый его тип обязательно принадлежал к другому классу, или даже нескольким; такое же требование может выдвигать экземпляр. Это аналоги множественного наследования. Есть и некоторые свойства, не имеющие аналогов в ООП. Например, реализация списка, как экземпляра класса сравнимых величин, требует, чтобы элементы списка также принадлежали к классу сравнимых величин.

Программистам, переходящим от ООП к ФП, следует знать важное отличие их системы классов. Если в ООП класс «привязан» к объекту, т. е. к данным, то в ФП — к функции. В ФП сведения о принадлежности к классу передаются при вызове функции, а не хранятся в полях объекта. Такой подход, в частности, позволяет решить проблему метода нескольких объектов (в ООП метод вызывается у одного объекта). Пример: метод сложения (чисел, строк) требует двух аргументов, причем одного типа.

[править]Неявная типизация

В некоторых языках программирования (например, в Python и Ruby) применяется так называемая утиная типизация[2] (другие названия: латентная, неявная), которая представляет собой разновидность сигнатурного полиморфизма. Таким образом, например, в языке Python полиморфизм не обязательно связан с наследованием.

Полиморфизм включения

Этот полиморфизм называют чистым полиморфизмом. Применяя такую форму полиморфизма, родственные объекты можно использовать обобщенно. С помощью замещения и полиморфизма включения можно написать один метод для работы со всеми типами объектов TPerson. Используя полиморфизм включения и замещения можно работать с любым объектом, который проходит тест «is-A». Полиморфизм включения упрощает работу по добавлению к программе новых подтипов, так как не нужно добавлять конкретный метод для каждого нового типа, можно использовать уже существующий, только изменив в нем поведение системы. С помощью полиморфизма можно повторно использовать базовый класс; использовать любого потомка или методы, которые использует базовый класс.

Параметрический полиморфизм

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

Параметрические методы

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

Параметрические типы

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

Полиморфизм переопределения

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

Полиморфизм-перегрузка

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

Операторы new и delete

Каждая программа во время работы получает определенное количество памяти, которую можно использовать. Такое выделение памяти под объекты во время выполнения называется динамическим, а сама память выделяется из хипа (heap). (Мы уже касались вопроса о динамическом выделении памяти в главе 1.) Напомним, что выделение памяти объекту производится с помощью оператора new, возвращающего указатель на вновь созданный объект того типа, который был ему задан. Например:

int *pi = new int;

размещает объект типа int в памяти и инициализирует указатель pi адресом этого объекта. Сам объект в таком случае не инициализируется, но это легко изменить:

int *pi = new int(1024);

Можно динамически выделить память под массив:

int *pia = new int[ 10 ];

Такая инструкция размещает в памяти массив встроенного типа из десяти элементов типа int. Для подобного массива нельзя задать список начальных значений его элементов при динамическом размещении. (Однако если размещается массив объектов типа класса, то для каждого из элементов вызывается конструктор по умолчанию.) Например:

string *ps = new string;

размещает в памяти один объект типа string, инициализирует ps его адресом и вызывает конструктор по умолчанию для вновь созданного объекта типа string. Аналогично

string *psa = new string[10];

размещает в памяти массив из десяти элементов типа string, инициализирует psa его адресом и вызывает конструктор по умолчанию для каждого элемента массива.
Объекты, размещаемые в памяти с помощью оператора new, не имеют собственного имени. Вместо этого возвращается указатель на безымянный объект, и все действия с этим объектом производятся посредством косвенной адресации.
После использования объекта, созданного таким образом, мы должны явно освободить память, применив оператор delete к указателю на этот объект. (Попытка применить оператор delete к указателю, не содержащему адрес объекта, полученного описанным способом, вызовет ошибку времени выполнения.) Например:

delete pi;

освобождает память, на которую указывает объект типа int, на который указывает pi. Аналогично

delete ps;

освобождает память, на которую указывает объект класса string, адрес которого содержится в ps. Перед уничтожением этого объекта вызывается деструктор. Выражение

delete [] pia;

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

21. ССЫЛКА (С++)

В языке программирования C++ ссылка — это простой ссылочный тип, менее мощный, но более безопасный, чем указатель, унаследованый от языка Си. Название C++ ссылка может приводить к путанице, так как в информатике под ссылкой понимается обобщенный концептуальный тип, а указатели и С++ ссылки являются специфическими реализациями ссылочного типа.

Введение

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

Например:

   /* конкретные адреса переменных могут быть другими */

   int a;  //переменная с именем "a" типа int размещена по адресу 0xbfd86d6c

   int &ra = a; //задано альтернативное имя (ra) для переменной по адресу 0xbfd86d6c

 

   /* символ "&" используемый для уже созданного объекта является операцией взятия адреса *

    * (и эта операция не есть ссылка), то есть &a тут означает получить адрес переменной *

    * к которому привязано имя "a"                                                   */

   cout << &a << ' \n ' << &ra << ' \n ';

В stdout будет записано:

0xbfd86d6c
0xbfd86d6c

То есть оба имени "a" и "ra" привязаны к одному и тому же адресу.

Ссылки нельзя объявлять без привязки к переменной (то есть не инициализировав при объявлении). После объявления ссылки её невозможно привязать к другой переменной.

Важно отличать ссылки от оператора взятия адреса & (address of). Оператор взятия адреса используется для уже созданного объекта с целью получить его адрес (то есть адрес области памяти, где хранятся значения), а ссылка это только задание альтернативного имени объекта (с точки зрения программиста, а не реализации). Например:

   int a;  //переменная типа int размещена по адресу 0xbfd86d6c с именем "a"

   int b = 3;

 

   /* создан указатель с именем "p" по адресу 0xbf971c4c, значение этого указателя *

    * адрес объекта с именем "a" - 0xbfd86d6c (это значение можно будет менять) */

   int *p = &a;

 

   p = &b; //присваиваем указателю новое значение соответствующее адресу переменной "b"

Отличие указателя от ссылки в том, что получить само значение переменной, на который указывает указатель, можно только выполнив операцию разыменовывания * (символ "*" в объявлении является объявлением указателя, а при применении к уже созданной переменной является оператором разыменовывания). Например:

   int a = 3;

   int *p = &a; //объявили, создали, и инициализировали объект

 

   // здесь к уже созданному объекту с именем "p" применяется оператор "*", который означает

   // считать значение из "p", которое является адресом и далее считать данные по этому адресу

   cout << *p << ' \n ';

В stdout будет записано:

3

Синтаксис и терминология

Объявление вида:

<Type> & <Name>

где <Type> — тип и <Name> — идентификатор, указывает идентификатор, чьим типом является ссылка на <Type>.

Примеры:

1. int A = 5;

2. int& rA = A;

3. extern int& rB;

4. int& foo ();

5. void bar (int& rP);

6. class MyClass { int& m_b; /*... */ };

7. int funcX() { return 42; }; int (&xFunc)() = funcX;

Здесь, rA и rB являются типами «ссылок на int», foo() — функция, возвращающая ссылку на int, bar() — функция с ссылкой в качестве параметра, которая ссылается на int, MyClass — класс (class) с членом, ссылающимся на int,funcX() — функция, возвращающая int, xFunc() — псевдоним для funcX.

Типы, относящиеся к «ссылка на <Type>», иногда называются ссылочными типами. Идентификаторы ссылочного типа называются ссылочными переменными. Называть их переменными в строгом смысле будет неправильно (показано дальше).

[править]Связь с указателями

C++ ссылки отличаются от указателей несколькими особенностями:

· Невозможно ссылаться напрямую на объект ссылочного типа после его определения; каждое упоминание его имени напрямую представляет объект, на который он ссылается.

· В качестве результата первого указания не могут быть выполнены никакие арифметические вычисления, приведения типов, взятия адреса и т.п.

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

· Ссылки не могут быть null (т.е.указывать в никуда), тогда как указатели - могут; каждая ссылка ссылается на некий объект, вне зависимости от его корректности.

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

Пример:

 

int& k; // компилятор выдаст сообщение: ошибка: 'k' declared as reference but not initialized ('k' объявлена как ссылка, но не инициализирована)


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

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

Кроме того, из-за ограничения операций над ссылками, они намного легче в понимании, чем указатели, а также более защищены от ошибок. Тогда как указатели могут стать некорректными благодаря множеству причин, начиная с указания на null-значения и выходов за границы и до использования недопустимых приведений типов, ссылка может стать некорректной лишь в двух случаях:

· Если она ссылается на объект с автоматическим размещением в памяти, с завершившимся временем жизни,

· Если она ссылается на объект, находящийся в блоке динамической памяти, который был освобожден.

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

Применение ссылок

· Помимо удобной замены указателям, еще одним полезным применением ссылок являются списки параметров функции, при помощи которых они могут передавать параметры, используемые для вывода без явного взятия адреса вызывающим. Например:

void square(int x, int& result) {

result = x * x;

}

Тогда следующий вызов поместит 9 в y:

square(3, y);

Тем не менее, следующий вызов приведет к ошибке компиляции, так как только параметры ссылки, не помеченные const, могут быть адресуемыми значениями:

square(3, 6);

· Возврат ссылки также позволяет непредусмотренный синтаксис, в котором вызовы функции могут быть присвоены:

int& preinc(int& x) { ++x; return x; }

preinc(y) = 5; // то же, что и ++y, y = 5

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

void f_slow(BigObject x) { /*... */

void f_fast(const BigObject& x) { /*... */ }

BigObject y;

f_slow(y); // медленно, копирует y в параметр x

f_fast(y); // быстро, дает прямой доступ к y (только для чтения)

_

Если f_slow() действительно требует собственной копии x для модификации, то она должна создать копию явным образом. Тогда как тот же способ может быть применен с использованием указателей, что вызовет модификацию каждого вызова функции, добавив громоздкий оператор взятия по адресу (&) в качестве аргумента, причем довольно сложно будет отменить изменения, если объект становится меньше.

· Причиной введения ссылок в язык С++ в основном являлась необходимость перегрузки операторов, применяемых к объектам пользовательских типов (классов). Как упоминалось выше, передача по значению громоздких объектов в качестве операндов вызывала бы лишние затраты на их копирование. С другой стороны, передача операндов по адресу с использованием указателей, приводит к необходимости взятия адресов операндов в выражениях. Например:

class BigClass

{

//...

friend BigClass operator-(BigClass* left, BigClass* right);

//...

};

 

BigClass x, y, z;

//...

x = &y - &z;

Однако, выражение &y - &z уже имело определённый смысл в языке С.

22. ЗНАКОМСТВО С КЛАССАМИ C++

Класс представляет собой главное инструментальное средство C++ для объектно-ориентированного программирования. Как вы узнаете из данного урока, класс очень похож на структуру, в которой сгруппированы элементы, соответствующие данным о некотором объекте, и оперирующие этими данными функции (называемые методами). Вы узнаете, что объект представляет собой некоторую сущность, например телефон. Класс C++ позволяет вашим программам определять все атрибуты объекта. В случае, когда объектом является телефон, класс может содержать такие элементы данных, как номер и тип телефона (кнопочный или дисковый) и функции, работающие с телефоном, например dial, answer, и hang_up. Группируя данные об объекте и кодируя их в одной переменной, вы упрощаете процесс программирования и увеличиваете возможность повторного использования своего кода. Этот урок знакомит вас с классами C++. К концу урока вы освоите следующие основные концепции:

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

 23-24. ПРЕДСТАВЛЕНИЕ ОБ ОБЪЕКТАХ И ОБЪЕКТНО-ОРИЕНТИРОВАННОМ ПРОГРАММИРОВАНИИ

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

Класс позволяет вашим программам группировать данные и функции которые выполняют операции над этими данными. Большинство книг и статей об объектно-ориентированном программировании называют функции класса методами. Подобно структуре, класс C++ должен иметь уникальное имя, за которым следует открывающая фигурная скобка, один или несколько элементов и закрывающая фигурная скобка:

class class_name

{
int data_member; // Элемент данных
void show_member(int); // Функция-элемент
};

После определения класса вы можете объявлять переменные типа этого класса (называемые объектами), как показано ниже:

class_name object_one, object_two, object_three;

Следующее определение создает класс employee, который содержит определения данных и метода:

class employee

{
public:
char name[64];
long employee_id;
float salary;
void show_employee(void)

{
cout << "Имя: " << name << endl;
cout << "Номер служащего: " << employee_id << endl;
cout << "Оклад: " << salary << endl;
};
};

В данном случае класс содержит три переменные и одну функцию-элемент. Обратите внимание на использование метки public внутри определения класса. Как вы узнаете из урока 22, элементы класса могут быть частными (private) или общими {public), от чего зависит, как ваши программы обращаются к элементам класса. В данном случае все элементы являются общими, это означает, что программа может обращаться к любому элементу, используя оператор точку. После определения класса внутри вашей программы вы можете объявить объекты (переменные) типа этого класса, как показано ниже:

————————————————————— Имя класса

employee worker, boss, secretary; //---------> Переменные класса (объекты)

Следующая программа EMPCLASS.CPP создает два объекта employee. Используя оператор точку, программа присваивает значения элементам данных Затем программа использует элемент show_employee для вывода информации о служащем:

#include <iostream.h>

#include <string.h>

class employee

{
public:
char name [64];
long employee_id;
float salary;
void show_employee(void)

{
cout << "Имя: " << name << endl;
cout << "Номер служащего: " << employee_id << endl;
cout << "Оклад: " << salary << endl;
};
};

void main(void)

{
employee worker, boss;
strcpy(worker.name, "John Doe");
worker.employee_id = 12345;
worker.salary = 25000;
strcpy(boss.name, "Happy Jamsa");
boss.employee_id = 101;
boss.salary = 101101.00;
worker.show_employee();
boss.show_employee();
}

Как видите, программа объявляет два объекта типа employee — worker и boss, а. затем использует оператор точку для присваивания значений элементам и вызова функции show_employee.

Представление об объектах

Большинство программ на C++ отражают реальные существующие объекты. В известном смысле объекты представляют собой сущности, например автомобиль, собаку, часы и т. д. Обычно объект имеет несколько атрибутов и операций, которые программа должна выполнять над этими атрибутами. Например, в случае часов свойства могут включать текущее время и время будильника. Операции такого объекта могли бы содержать установку времени, установку будильника или выключение будильника. При объектно-ориентированном программировании ваши программы фокусируются на объектах и операциях над этими объектами.

Методы класса

Классы C++ позволяют вашим программам группировать данные объекта и функции объекта (методы), которые оперируют с этими данными, в одной переменной. У вас есть две возможности определения методов объекта. Первая состоит в том, что вы можете включить весь код функции внутрь определения класса. Несмотря на то что включение кода метода в определение класса может представляться удобным, однако, когда классы становятся сложнее и включают несколько методов, операторы функций могут вносить беспорядок в определение классов. Таким образом, многие программы определяют операторы функции вне класса. В определение класса программа должна включать прототип функции, который указывает имя функции, тип возвращаемого значения и типы параметров.

Для определения функции вне определения класса ваша программа должна предварять определение функции именем класса и оператором глобального разрешения, как показано ниже:

return_type class_name::function_name(parameters)
{ // Операторы }

ВТОРОЙ ПРИМЕР

Следующая программа PEDIGREE.CPP создает класс dog, который содержит несколько полей данных и функцию show_breed. Программа определяет функцию класса вне определения самого класса. Затем программа создает два объекта типа dog и выводит информацию о каждой собаке:

#include <iostream.h>

#include <string.h>

class dogs

{
public:
char breed[64];
int average_weight;
int average_height;
void show_dog(void);
};

void dogs::show_breed(void)

{
cout << "Порода: " << breed << endl;
cout << "Средний вес: " << average_weight << endl;
cout << "Средняя высота: " << average_height << endl;
}

void main(void)

{
dogs happy, matt;
strcpy(happy.breed, "Долматин");
happy.average_weight = 58;
happy.average_height = 24;
strcpy(matt.breed, "Колли");
matt.average_weight =22;
matt.average_height = 15;
happy.show_breed();
matt.show_breed();
}

ЧТО ВАМ НЕОБХОДИМО ЗНАТЬ

Программы на C++ широко используют классы. Говоря кратко, классы позволяют вашим программам группировать для определенного объекта его данные и методы (функции), которые оперируют этими данными, в одной переменной. Как видите, классы очень похожи на структуры, которые вы рассматривали в уроке 18. Классы C++ являются основой объектно-ориентированного программирования. Следующие уроки расширяют возможности предоставляемые классами. Упоминавшаяся в этом уроке метка public, по являющаяся в определении класса, делает элементы класса доступными для всей программы. Из урока 22 вы больше узнаете о частных и общих элементах класса. До изучения урока 22 убедитесь, что освоили следующие основные концепции:

    1. В известном смысле объект представляет собой сущность, с которой ваши программы выполняют разные операции.
    2. Программы на C++ представляют объекты посредством классов.
    3. Класс, подобно структуре, содержит элементы. Элементы класса могут хранить информацию (данные) или быть функциями (методами), которые оперируют этими данными.
    4. Каждый класс имеет уникальное имя.
    5. После определения класса вы можете объявлять объекты этого класса, используя имя класса в качестве типа.
    6. Для обращения к элементам класса (как к данным, так и к функциям) ваши программы используют оператор точку.
    7. Программы могут определять функцию класса внутри или вне определения класса. Если вы определяете функцию вне определения класса, вам следует указать имя класса и использовать оператор глобального разрешения, например

25.    Inline Функции

Чтобы избежать расходов на вызов функции, функцию можно описать как inline, а чтобы обеспечить более быстрый доступ к параметрам, их можно описать как register.

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

inline-функция - это функция с модификатором ilnline, код которой компилятор может встроить в место вызова функции. Например

код C++
1:

2:

3:

4:

5:

6:

7:

8:

9:

10:

inline int func(void)

{

return 5;

}

int main(void)

{

int n = func();

//...

}


Компилятор может встроить код функции, примерно так:

код C++
1:

2:

3:

4:

5:

int main(void)

{

int n = 5; // нет функции, есть её код

//...

}


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

"накладные расходы на вызов ф-ции" - не пустой звук! Например, ф-ция которая вычисляет Max из двух-трёх значений, в inline-варианте будет работать на 10-25% быстрее, т.к. не-inline функция потребует создания кадра стека и сопутствующих действий (пролог-эпилог), а inline - "прозрачно", т.е. прямо в коде текущей функции выполнит все свои действия.

26.               Указатель this

В C++ существует специальный указатель this. Для "процедурных программистов" эта тема является довольно "тугой" для понимания, поэтому немного о том, что такое указатель this в контексте нашей серии статей.

Экземпляр объекта, как мы рассматривали в статье "Введение в теорию компиляции. И выведение из неё...", есть семантическое соединение двух сущностей - данных, располагаемых в сегменте данных и методов, располагаемых в сегменте кода. Данные объекта могут изменяться во время жизни объекта, а методы - нет. Методы целиком определяются его статическим типом и все экземпляры данного статического типа разделяют один и тот же экземпляр кода методов имея при этом свой экземпляр данных.

При этом в C++ различаются статические методы класса и нестатические методы класса, причём утверждается, что только нестатические методы класса могут обращаться к нестатическим же данным класса. Что это такое и почему существует это правило?

А правило говорит вот о чём - если класс определяет данные, которые у каждого экземпляра объекта будут индивидуальны, то обращаться к таким индивидуальным данным могут только нестатические методы. Статическим методам к ним обращаться нельзя.

Чем отличаются оба типа методов? Только одним - нестатические методы имеют скрытый параметр, который им подставляет компилятор, когда генерирует вызов - в этом параметре компилятор передаёт адрес структуры данных, соответствующих данному экземпляру объекта. А у статических методов такого параметра нет, поэтому они и не могут адресоваться к индивидуальным данным экземпляра.

Вот этот-то самый указатель, именующий индивидуальную структуру данных для каждого объекта и называется "указатель this". Семантически он соответствует адресу "всего экземпляра объекта". И когда компилятор компилирует код метода класса, то все ссылки на свои данные экземпляра (например, на переменную "x") компилятор по умолчанию трактует как this->x. Т.о. внутри метода класса this есть "указатель на самого себя". Естественно, что внутри каждого экземпляра всякого класса this будет иметь своё значение - его заранее не знает программист, зато - очень хорошо знает компилятор. И его обязательно получит и любой нестатический метод объекта.

Но сам метод - "не обслуживается" this. Методы-то класса всегда разделяются всеми экземплярами класса. И уничтожаются - только данные класса, а методы неуничтожимы. Поэтому вызов конструкции delete this вполне корректен, он заставит метод класса разрушить данный экземпляр данных (объект) класса, но не сам этот метод - другие экземпляры того же класса не п


Поделиться с друзьями:

Типы сооружений для обработки осадков: Септиками называются сооружения, в которых одновременно происходят осветление сточной жидкости...

Своеобразие русской архитектуры: Основной материал – дерево – быстрота постройки, но недолговечность и необходимость деления...

Автоматическое растормаживание колес: Тормозные устройства колес предназначены для уменьше­ния длины пробега и улучшения маневрирования ВС при...

Эмиссия газов от очистных сооружений канализации: В последние годы внимание мирового сообщества сосредоточено на экологических проблемах...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.162 с.