Использование пользовательских событий в программах 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Использование пользовательских событий в программах



 

Для организации событийного управления объектами, определенными пользователями необходимо:

1. Объявить делегат.

2. Создать класс-издатель, содержащий:

– событие, соответствующее объявленному делегату;

– метод, включающий логику порождения этого события и собственно его генерацию.

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

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

 

Пример 14.18. Комплексное использование различных членов класса и событий, определенных пользователем.

 

#include "stdafx.h"

using namespace System;

// Объявление делегатов

public delegate void NameChangedHandler(String^ NewName);

public delegate void AgeChangedHandler(short NewAge);

public ref class Human { // Определение класса Human

private:

String^ HumanName;  // Определение поля Имя

short HumanAge;     // Определение поля Возраст

public:

Human () { HumanName = ""; HumanAge = 0; } // Конструктор

// Объявление событий смены имени и возраста

event NameChangedHandler^ NameChanged;

event AgeChangedHandler^ AgeChanged;

property String ^ Name { // Определение свойства Имя

String ^ get () {

   return HumanName; // Возврат значения, хранимого в поле

}

void set(String^ value) {

   HumanName = value; // Сохранение значения в поле

   NameChanged (value); // Порождение события смены имени

}

}

property short Age { // Определение свойства Возраст

short get () {

   return HumanAge; // Возврат значения, хранимого в поле

}

void set(short value) {

   if (value < 0 || value > 200) {

      // Вывод сообщения об ошибке изменения возраста

      Console:: WriteLine ("Возраст должен быть между " +

                         "0 и 200 годами");

   }

   else

      if (HumanAge!= value) {

         HumanAge = value; // Сохранение значения в поле

         // Порождение события смены возраста

         AgeChanged (value);

      }

}

}

void OutputName () { // Определение метода вывода имени

    Console:: WriteLine ("ФИО: {0}", HumanName);

}

};

public ref class OutputHumanData { // Определение класса

public:

void NameChanged(String^ name) {

// Обработка события смены имени

Console:: WriteLine ("Имя изменено на {0}", name);

}

void AgeChanged (short age) {

// Обработка события смены возраста

Console:: WriteLine ("Возраст изменен на {0}", age);

}

};

int main () {

Human ^ h = gcnew Human; // Создание объекта класса Human

OutputHumanData ^ ohd = gcnew OutputHumanData;

                    // Создание объекта класса OutputHumanData

// Подписка на события и определение функций-обработчиков

h->NameChanged += gcnew NameChangedHandler(ohd,

                       &OutputHumanData::NameChanged);

h->AgeChanged += gcnew AgeChangedHandler(ohd,

                      &OutputHumanData::AgeChanged);

Console::WriteLine(" Имя: {0}, возраст: {1}", h->Name, h->Age);

                 // Вывод имени и возраста (исходные значения)

h -> Name = "Иванов А.П."; // Изменение имени. Вызывает

                           // обработку события изменения имени

h -> OutputName ();       // Вывод имени

h -> Age = 25;       // Изменение возраста. Вызывает

                       // обработку события изменения возраста

Console:: WriteLine ("Возраст: {0}", h -> Age); // Вывод возраста

h -> Age = 25;           // Не вызывает обработку события,

                           // т.к. возраст не изменился

h->Age = -20;          // Не вызывает обработку события,

                           // т.к. возраст не может быть

                           // отрицательным. Выводит сообщение

Console:: WriteLine ("Возраст: {0}", h -> Age);

                       // Вывод возраста. Результат не изменен

Console:: ReadLine ();

return 0;

}

/* Вывод:

Имя:, возраст: 0

Имя изменено на Иванов А.П.

ФИО: Иванов А.П.

Возраст изменен на 25

Возраст: 25

Возраст должен быть между 0 и 200 годами

Возраст: 25

*/

 

Функции, объявленные в классе OutputHumanData и являющиеся обработчиками событий, могут и не входить в этот класс, а объявляться в области видимости файла вместе с основной функцией main(). В этом случае внутри функции main() не нужно объявлять объект класса, который должен был содержать функции-обработчики. Вследствие этого в операторе добавления делегату функции-обработчика этот делегат будет иметь только один параметр, которым является имя функции обработки соответствующего события. Например:

 

h->NameChanged += gcnew NameChangedHandler(NameChanged);

h->AgeChanged += gcnew AgeChangedHandler(AgeChanged);

 

Пример 14.19. Предположим, нужно определить класс, в котором имеется метод, считающий от 0 до 100. Но когда счетчик досчитает, например, до 69 функция-обработчик этого события должна вывести на экран сообщение «Пора действовать, уже 69!».

 

#include "stdafx.h"

using namespace System;

public ref class ClassCounter { // Определение класса-издателя

public:

  // Объявление делегата внутри класса-издателя

delegate void MethodContainer ();

// Объявление события OnCount c типом делегата MethodContainer

event MethodContainer^ OnCount;

void Count() {          // Метод, инициирующий событие

    for (int i = 0; i <= 100; i++) { // Счет до 100

        if (i == 69) {           // Если i = 69, то...

           OnCount ();            // Порождение события

        }

    }

    Console:: WriteLine ("Счет завершен!");

}

};

void Message () {                // функция-обработчик события

Console:: WriteLine ("Пора действовать, уже 69!");  // Сообщение

}

int main() {

ClassCounter^ counter = gcnew ClassCounter;

                       // Создание объекта класса ClassCounter

// Подписка на событие и определение функции-обработчика

counter->OnCount += gcnew ClassCounter::MethodContainer(Message);

counter->Count();      // Запуск счетчика до 100

Console::ReadLine();

return 0;

}

/* Вывод:

 Пора действовать, уже 69!

Счет завершен!

*/

 

 

Наследование

 

Одиночное наследование

Пример 14.20. Создание иерархии классов при наследовании.

 

#include "stdafx.h"

using namespace System;

class A {       // Базовый класс

public:

void func1() { Console::WriteLine("A::func1()"); } // Метод

};

class B: public A { // Класс B наследуется от класса A

public:

void func2() { Console::WriteLine("B::func2()"); } // Метод

};

class C: public B { // Класс С наследуется от классов А и B

public:

void func3() { Console::WriteLine("C::func3()"); } // Метод

};

int main () {

C c;   // Создание экземпляра класса C

c.func1(); // Вывод: A::func1()

c.func2(); // Вывод: B::func2()

c.func3(); // Вывод: C::func3()

Console:: ReadLine ();

return 0;

}

 

При наследовании используется следующий формат объявления класса:

 

class <Производный_класс: [<Спецификатор_доступа>] <Базовый_класс {

<Объявления_членов_класса>;

} [<Объявления_переменных_через_запятую>];

 

В параметре <Спецификатор_доступа> можно указать следующие спецификаторы:

public – открытое наследование. Все открытые и защищенные члены базового класса становятся соответственно открытыми и защищенными членами производного класса;

private – закрытое наследование. Все открытые и защищенные члены базового класса становятся закрытыми членами производного класса;

protected – защищенное наследование. Все открытые и защищенные члены базового класса становятся защищенными членами производного класса.

 

При использовании закрытого наследования можно изменить уровень доступа некоторых открытых и защищенных членов базового класса. Сделать это можно двумя способами:

 

<Базовый_класс>::<Член_класса>;

using <Базовый_класс>::<Член_класса>;

 

Пример 14.21. Изменение уровня доступа отдельных членов класса при закрытом наследовании.

 

#include "stdafx.h"

using namespace System;

class A {        // Базовый класс

public:

void func1() { Console::WriteLine("A::func1()"); }

void func2() { Console::WriteLine("A::func2()"); }

void func3() { Console::WriteLine("A::func3()"); }

};

class B: private A { // Закрытое наследование класса

public:

A::func2;    // Открываем доступ к func 2()

using A::func3; // Открываем доступ к func 3()

void func4() { func1(); }

};

int main() {

B obj;

// obj.func1(); // Ошибка. Метод func 1() – закрытый член класса

obj. func 2(); // Вывод: A:: func 2()

obj.func3(); // Вывод: A::func3()

obj.func4(); // Вывод: A::func1(). Вызов метода func 1()

                                     // из B:: func 4()

Console:: ReadLine ();

return 0;

}

 

Для передачи параметров конструкторам базовых классов используется следующий синтаксис:

 

<Название_конструктора_производного_класса>(<Параметры>):

     <Название_конструктора_базового_класса>(<Значения>) {

// Тело конструктора

}

 

 

Пример 14.22. Переопределение метода при наследовании класса.

 

#include "stdafx.h"

using namespace System;

class A {       // Базовый класс

public:

void func1() { Console::WriteLine("A::func1()"); } // Метод

void func2() { Console::WriteLine("A::func2()"); } // Метод

};

class B: public A { // Класс B наследуется от класса A

public:

void func2() { Console::WriteLine("B::func2()"); }

                                // Переопределенный метод

void func3() { Console::WriteLine("B::func3()"); } // Метод

};

int main () {

B b;   // Создание экземпляра класса B

b. func 1(); // Вызов унаследованного метода func 1()

b. func 2(); // Вызов переопределенного метода func 2()

b. func 3(); // Вызов добавленного в класс метода func 3()

Console::ReadLine();

return 0;

}

/* Вывод:

Вывод: A::func1()

Вывод: B::func2()

Вывод: B::func3()

*/

 

Для указания, что класс не может использоваться в качестве базового класса, применяется ключевое слово sealed в объявлении класса. Например:

 

public sealed class A {}

 

Указание, что класс может использоваться только в качестве базового класса и нельзя создать экземпляр этого класса, можно записать следующим образом:

 

public abstract class B {}

 

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

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

 

#include "stdafx.h"

using namespace System;

class A {       // Базовый класс

public:

int x;

A(int a) {                          // Конструктор

x = a;

Console::WriteLine("A::A()");

}

~A() { Console::WriteLine("A::~A()"); } // Деструктор

};

class B: public A { // Класс B наследуется от класса A

public:

int y;

B (int a, int b): A (b) {            // Конструктор

y = a;

Console:: WriteLine (" B:: B ()");

}

~ B () { Console:: WriteLine (" B::~ B ()"); } // Деструктор

};

class C: public B { // Класс C наследуется от классов A и B

public:

int z;

C (int a, int b, int с): B (b, с) {      // Конструктор

z = a;

Console:: WriteLine (" C:: C ()");

}

~ C () { Console:: WriteLine (" C::~ C ()"); } // Деструктор

};

int main () {

C obj (10, 20, 30);

Console:: WriteLine (" A:: x = {0} B:: y = {1} C:: z = {2}",

                  obj. x, obj. y, obj. z);

Console:: ReadLine ();

return 0;

}

/* Вывод (для просмотра после вывода нажмите < Ctrl + F 5>):

A::A()

B::B()

 C::C()

A::x = 30  B::y = 20  C::z = 10

C::~ C ()

B::~ B ()

A::~ A ()

*/

 

 

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

При множественном наследовании используется следующий формат объявления производного класса:

 

class <Производный_класс>:

              [<Спецификатор_доступа>] <Базовый_класс1>,

              [<Спецнфнкатор_доступа>] <Базовый_класс2>,

             ...,

              [<Спецификатор_доступа>] <Базовый_класс N > {

<Объявления_членов_класса>;

} [<Объявления_переменных_через_запятую>];

 

Для передачи параметров конструкторам базовых классов используется следующий синтаксис:

 

<Название_конструктора_производного_класса>(<Параметры>):

     <Название_конструктора_базового_класса1>(<Значения>),

     <Название_конструктора_базового_класса2>(<3начения>),

    ...,

     <Название_конструктора_базового_класса3>(<Значения>) {

// Тело конструктора

}

 

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

 

#include "stdafx.h"

using namespace System;

class A {      // Базовый класс

public:

int x;

A(int a) {     // Конструктор

x = a;

Console::WriteLine("A::A()");

}

~A() { Console::WriteLine("A::~A()"); } // Деструктор

void func1() { Console::WriteLine("A::func1()"); } // Метод

};

class B {      // Базовый класс

public:

int y;

B(int a) { // Конструктор

y = a;

Console::WriteLine("B::B()");

}

~B() { Console::WriteLine("B::~B()"); } // Де структор

void func2() { Console::WriteLine("B::func2()"); } // Метод

};

class C: public A, public B { // Класс C наследуется от классов A и B

public:

int z;

C (int a, int b, int c): A (b), B (c) {   // Передаем значения

z = a;

Console:: WriteLine (" C:: C ()");

}

~ C () { Console:: WriteLine (" C::~ C ()"); } // Деструктор

};

int main () {

C obj (10, 20, 30);

obj. func 1();

obj. func 2();

Console:: WriteLine (" A:: x = {0} B:: y = {1} C:: z = {2}",

              obj. x, obj. y, obj. z);

Console:: ReadLine ();

return 0;

}

/* Вывод (для просмотра после вывода нажмите < Ctrl + F 5>):

A::A()

 B::B()

 C::C()

 A::func1()

 B::func2()

A::x = 20  B::y = 30  C::z = 10

C::~ C ()

B::~ B ()

A::~ A ()

*/

 

Пример 14.25. Неоднозначность при множественном наследовании.

 

#include "stdafx.h"

using namespace System;

class A {                      // Базовый класс

public:

void func1() { Console::WriteLine("A::func1()"); } // Метод

};

class B: public A {       // Класс B наследуется от класса A

public:

void func2() { Console::WriteLine("B::func2()"); } // Метод

};

class C: public A {       // Класс C наследуется от класса A

public:

void func3() { Console::WriteLine("C::func3()"); } // Метод

};

class D: public B, public C { // Класс D наследуется от классов

public:                       // A, B и C

};

int main() {

D obj;

// obj.func1(); // Неоднозначность. Из B или из C?

obj.B::func1(); // Берем из класса B

obj.C::func1(); // Берем из класса C

obj.func2(); // Однозначно

obj.func3(); // Однозначно

Console::ReadLine();

return 0;

}

/* Вывод:

A::func1()

A::func1()

B::func2()

C::func3()

*/

 

Один из способов разрешения неоднозначности является явное указание класса и оператора :: перед названием метода при вызове:

 



Поделиться:


Последнее изменение этой страницы: 2019-05-20; просмотров: 144; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.147.103.202 (0.188 с.)