Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
OOP_otvety_k_ekzamenu.doc
Скачиваний:
55
Добавлен:
13.04.2015
Размер:
786.94 Кб
Скачать

34. Понятие интерфейса. Применение интерфейсов.

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

В С++ же интерфейс - это скорее особый термин при рассуждении, типовое решение, а не явная языковая конструкция.

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

Пример:

Следующее объявление вводит интерфейс слушателя изменений котировок биржевых акций (Stock):

stock_change_listener.hpp

#ifndef _STOCK_CHANGE_LISTENER_HPP_

#define _STOCK_CHANGE_LISTENER_HPP_

class Stock;

class StockChangeListener

{

public:

// Сообщение об изменении цены котировки указанных акций

virtual void onPriceChanged ( Stock & _s, double _oldPrice ) = 0;

};

#endif // _STOCK_CHANGE_LISTENER_HPP_

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

stock.hpp

#ifndef _STOCK_HPP_

#define _STOCK_HPP_

#include <vector>

class StockChangeListener;

// Класс, представляющий биржевую котировку акций

class Stock

{

private:

// Набор слушателей изменений цены акций

std::vector< StockChangeListener * > m_listeners;

// Название котировки ценных бумаг

const char * m_title;

// Текущая цена

double m_currentPrice;

private:

// Запрещенные конструктор копий и оператор присвоения

Stock ( const Stock & _s );

Stock & operator = ( const Stock & _s );

public:

// Конструктор

Stock ( const char * _title, double _initialPrice );

// Метод доступа к названию котировки

const char * getTitle () const;

// Метод доступа к текущей цене

double getCurrentPrice () const;

// Метод регистрации слушателея изменения цены

void addChangeListener ( StockChangeListener & _listener );

// Метод обновления цены

void updatePrice ( double _newPrice );

};

// Реализация метода доступа к названию котировки

inline const char *

Stock::getTitle () const

{

return m_title;

}

// Реализация метода доступа к текущей цене

inline double

Stock::getCurrentPrice () const

{

return m_currentPrice;

}

#endif // _STOCK_HPP_

35. Множественное наследование конкретных классов. Синтаксис, структура в памяти, особенности применения и реализации.

С++, в отличие от многих других объектно-ориентированных языков, допускает множественное наследование от конкретных классов. Базовые классы следует перечислять через запятую. Класс, производный от двух и более конкретных классов, объединяет свойства всех базовых. Такие классы иногда называют “примесями” (mixin).

imagebutton.hpp

#ifndef _IMAGEBUTTON_HPP_

#define _IMAGEBUTTON_HPP_

//************************************************************************

#include "button.hpp"

#include "imagecontrol.hpp"

//************************************************************************

// Класс, представляющий кнопку с изображением

class ImageButton

: public Button, public ImageControl

{

//------------------------------------------------------------------------

public:

// Конструктор - передаются аргументы для двух базовых классов

ImageButton ( const std::string & _text, const std::string & _imageFilename );

// Переопределение метода отрисовки из базового класса Button

void draw ( int _x, int _y ) override;

//------------------------------------------------------------------------

};

//************************************************************************

#endif // _IMAGEBUTTON_HPP_

imagebutton.cpp

//************************************************************************

#include "imagebutton.hpp"

//************************************************************************

ImageButton::ImageButton ( const std::string & _text,

const std::string & _imageFilename )

// Вызов конструкторов двух базовых классов

: Button( _text )

, ImageControl( _imageFilename )

{

}

//************************************************************************

// Реализация переопределенного метода отрисовки кнопки

void ImageButton::draw ( int _x, int _y )

{

// Вычисляем размеры текста

int width, height;

calculateTextSize( width, height );

// Изображение выводится слева от текста, понадобится 3 отступа

width += getImageWidth() + 3 * PADDING;

// Высота кнопки - максимум из высоты изображения и текста + 2 отступа

if ( getImageHeight() > height )

height = getImageHeight();

height += 2 * PADDING;

// Отрисовка фонового прямоугольника (вызов Button::drawRectangle)

drawRectangle( _x, _y, width, height );

// Отрисовка изображения (вызов ImageConrol::drawImage)

drawImage( _x + PADDING,_y + PADDING );

// Отрисовка текста на кнопке правее изображения (вызов Button::drawText)

drawText( _x + getImageWidth() + 2 * PADDING, _y + PADDING );

}

Структура размещения данных класса-примеси ImageButton в памяти предполагает, что в начале будут размещаться данные первого базового класса Button, затем второго ImageControl. Если бы ImageButton содержал собственные дополнительные поля, они бы размещались в объекте после полей обоих базовых классов:

Указатель или ссылка на объект класса ImageButton может быть преобразован к указателю/ссылке на объект любого из двух базовых классов:

ImageButton ib( “OK”, “ok.png” );

Button * pButton = & ib;

ImageControl * pControl = & ib;

Интересно, что, несмотря на манипулирование одним и тем же объектом ImageButton, абсолютные значения адресов в преобразованных указателях pButton и pControl совпадать не будут. Это вытекает из расположения полей базовых классов в памяти объекта. Поля первого базового класса находятся в начале объекта, и адрес pButton будет совпадать с адресом начала объекта. Но поля второго базового класса смещены от начала объекта на размер первого базового класса. Соответственно, этот адрес не совпадает с адресом начала объекта. Учитывая факт возможного смещения адресов в иерархии множественного наследования, следует всячески избегать попыток преобразования вниз по такой иерархии.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]