ПЕРЕЛІК ДИСЦИПЛІН:
  • Адміністративне право
  • Арбітражний процес
  • Архітектура
  • Астрологія
  • Астрономія
  • Банківська справа
  • Безпека життєдіяльності
  • Біографії
  • Біологія
  • Біологія і хімія
  • Ботаніка та сільське гос-во
  • Бухгалтерський облік і аудит
  • Валютні відносини
  • Ветеринарія
  • Військова кафедра
  • Географія
  • Геодезія
  • Геологія
  • Етика
  • Держава і право
  • Цивільне право і процес
  • Діловодство
  • Гроші та кредит
  • Природничі науки
  • Журналістика
  • Екологія
  • Видавнича справа та поліграфія
  • Інвестиції
  • Іноземна мова
  • Інформатика
  • Інформатика, програмування
  • Юрист по наследству
  • Історичні особистості
  • Історія
  • Історія техніки
  • Кибернетика
  • Комунікації і зв'язок
  • Комп'ютерні науки
  • Косметологія
  • Короткий зміст творів
  • Криміналістика
  • Кримінологія
  • Криптология
  • Кулінарія
  • Культура і мистецтво
  • Культурологія
  • Російська література
  • Література і російська мова
  • Логіка
  • Логістика
  • Маркетинг
  • Математика
  • Медицина, здоров'я
  • Медичні науки
  • Міжнародне публічне право
  • Міжнародне приватне право
  • Міжнародні відносини
  • Менеджмент
  • Металургія
  • Москвоведение
  • Мовознавство
  • Музика
  • Муніципальне право
  • Податки, оподаткування
  •  
    Бесплатные рефераты
     

     

     

     

     

     

         
     
    Інтерфейси як вирішення проблем множинного спадкоємства
         

     

    Інформатика, програмування

    Інтерфейси як вирішення проблем множинного успадкування

    Євген Каратаєв

    В цій роботі розбирається проблема множинного спадкоємства в мові програмування С + + і можливе її вирішення шляхом застосування абстракцій інтерфейсів.

    Множинні спадкуванням є утворення класу шляхом успадкування одночасно декількох базових класів. Штука корисна і одночасно з цим проблемна. Розглянемо приклад, в якому з'являється множинне успадкування, що приводить до проблеми.

    Класичним завданням для починаючого програміста є завдання написати класи, реалізують ієрархію Людина - Студент - Співробітник. Зазвичай першим же рішенням є утворення трьох класів у вигляді:

    class Людина (... );

    class Співробітник: public Людина (... );

    class Студент: public Людина (... );

    В класі Людина декларуються кілька віртуальних і, можливо, абстрактних, функцій, які перевизначаються/реалізуються в класах-спадкоємців. Схема на перший погляд, цілком очевидна і практично ні в кого не викликає підозр. Схема реалізується в програмі і програма здається в роботу.

    Проблема виникає пізніше, коли оператор приходить і говорить:

    -- У мене є людина, яка одночасно і співробітник і студент. Що мені робити?

    Реалізована схема, взагалі кажучи, не передбачає такого варіанту - можуть бути або співробітник, або студент. Але щось робити треба. У цей момент приходить на допомога множинне успадкування. Програміст, не довго думаючи, створює ще один клас, утворений спадкуванням і від Співробітник і від Студент:

    class СтудентСотруднік: public Студент, public Співробітник (...};

    На перший погляд все гаразд, на другому - повний бардак. Справа в тому, що клас Співробітник, як він був декларував, містить у собі повну копію класу Людина. Те ж саме відноситься і до класу Студент. Таким чином, клас СтудентСотруднік буде містити в собі вже 2 копії класу Людина. При цьому функції класу Співробітник будуть працювати зі своїм екземпляром класу Людина, а функції класу Студент - зі своїм. У результаті коректної поведінки домогтися практично дуже важко. У класі СтудентСотруднік доведеться перевизначати все функції базових класів і викликати відповідні функції базових класів, щоб модифікації обох копій класу Людина пройшли когерентно.

    Виявивши таку ситуацію шляхом важкої налагодження, програміст приходить до необхідності застосування віртуального наслідування для виключення дублювання класу Людина. Проблема полягає в тому, що віртуальне наслідування вимагає модифікації графа успадкування базових класів. Необхідна схема має вигляд:

    class Людина (... );

    class Студент: virtual public Людина (... );

    class Співробітник: virtual public Людина (... );

    class СтудентСотруднік: public Студент, public Співробітник (...

    );

    В цьому варіанті вирішена проблема однозначної входимо класу Людина в усі класи. Але залишається питання - чи не виникне такої ж проблеми і далі з отриманим класом СтудентСотруднік? І чи буде можливість виробити модифікацію вже працюючого коду? У такій ситуації руки можуть опуститися -- слід або погодитися з існуванням проблемного коду або дійсно йти на повну переробку програми.

    Тим не менш елегантне рішення існує. Це реалізація базових класів з принципом інтерфейсів. Мова С + + не містить мовної підтримки інтерфейсів в явному вигляді, тому будемо їх емулювати. Принцип інтерфейсу полягає в тому, що його завданням є не стільки реалізація класу, скільки його декларація. Нормалізуючи вихідну завдання:

    class БитьЧеловеком (... );

    class БитьСтудентом (... );

    class БитьСотрудніком (... );

    Виходячи з нормалізованого безлічі класів, отримаємо доповнення:

    class Людина: public БитьЧеловеком (... );

    class );

    class Студент: public БитьЧеловеком, public БитьСтудентом (...};

    class СтудентСотруднік: public БитьЧеловеком, public БитьСтудентом,

    public БитьСотрудніком (... );

    Формально кажучи, така схема побудови класів цілком працездатна за винятком того, що в багатьох випадках програмісти відносяться до інтерфейсів надто вже буквально - залишають у них тільки абстрактні функції і реалізують ці функції тільки в класах-спадкоємців. В результаті повністю вихолощується ідея повторного використання коду. Підставою для нереалізації функцій у інтерфейсних класах зазвичай служить те, що в класі - інтерфейсі немає "ядра" об'єкта. У нашому випадку ядром об'єкта або класом, які реалізують можливість існування об'єкта, може виступати клас БитьЧеловеком.

    Можливим вирішенням проблеми є передача конструктору інтерфейсного класу покажчика на конструюються об'єкт з тим, щоб його запам'ятати в своєму приватному поле даних і використовувати при реалізації функцій інтерфейсу. Приблизно за схемою:

    class БитьСтудентом

    (

    БитьЧеловеком & m_БитьЧеловеком;

    public:

    БитьСтудентом (БитьЧеловеком & init)

    : m_БитьЧеловеком (init)

    (... );

    );

    class Студент: public БитьЧеловеком, public БитьСтудентом

    (

    public:

    Студент ()

    : БитьЧеловеком (), БитьСтудентом (* this)

    (...};

    );

    В цією схемою, відповідно до стандарту, також є проблема - стандарт не гарантує ініціалізації конструкторів, зазначених у списку ініціалізації, в тому порядку, в якому вони перераховані в цьому списку. Тому ми, передаючи * this як аргумент конструктора базового класу, отримуємо посилання на Негарантований певний об'єкт. Вийти з цієї ситуації можна, якщо декларувати конструктор без аргументів і створити додаткову функцію ініціалізації, що залежить від * this. Але дублювання посилань, що зберігаються в інтерфейсних класах, тим не менше, зберігається і це є негарно.

    Для вирішення цього завдання є надзвичайно гарне, на мій погляд, рішення. Рішення полягає в тому, щоб не зберігати посилання на ядро об'єкта, а отримувати її динамічно. Для цього застосовується оператор приведення типу dynamic_cast, застосовується не до класу, а до об'єкта в процесі роботи програми. Приклад:

    class БитьСтудентом

    (

    public:

    БитьСтудентом (){};

    virtual void Func (void);

    // приклад функції, що звертається до ядра об'єкта

    (

    БитьЧеловеком * ptr = dynamic_cast < БитьЧеловеком *> (this);

    if (ptr)

    (

    // використовуємо ядро

    )

    );

    );

    На перший погляд, приведення типу БитьСтудентом до типу БитьЧеловеком неможливо, оскільки ніхто з цих класів ні від кого не наслідувати. Але справа в тому, що оператор dynamic_cast визначений не для класів, а для об'єктів. І якщо при виконанні коду Func реальний об'єкт, для якого ця функція виконується, имееет клас, успадкування від БитьЧеловеком, то оператор поверне правильне значення. Відповідно до стандарту, оператор приведення типу dynamic_cast має два виду поводження якщо приведення неможливо - повернути нульове значення або порушити виняткову ситуацію. Обидва варіанти нас повністю влаштовують.

    Я вважаю, що в моделі застосування інтерфейсних класів для вирішення проблем множинного спадкування буде також гарно побудувати інтерфейсні класи з конструкторами, що не вимагають звернення до ядра об'єкта. Втім, це вже з галузі філософії перешкодостійкого програмування.

    Список літератури

    Для підготовки даної роботи були використані матеріали з сайту http://karataev.nm.ru/

         
     
         
    Реферат Банк
     
    Рефераты
     
    Бесплатные рефераты
     

     

     

     

     

     

     

     
     
     
      Все права защищены. Reff.net.ua - українські реферати ! DMCA.com Protection Status