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

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

imagebutton.hpp

#ifndef _IMAGEBUTTON_HPP_

#define _IMAGEBUTTON_HPP_

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

#include "button.hpp"

#include "imagecontrol.hpp"

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

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

classImageButton

: publicButton, publicImageControl

{

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

public:

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

ImageButton ( conststd::string & _text, conststd::string & _imageFilename );

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

voiddraw ( int_x, int_y ) override;

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

};

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

#endif // _IMAGEBUTTON_HPP_

imagebutton.cpp

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

#include "imagebutton.hpp"

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

ImageButton::ImageButton ( conststd::string & _text,

conststd::string & _imageFilename )

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

: Button( _text )

, ImageControl( _imageFilename )

{

}

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

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

voidImageButton::draw ( int_x, int_y )

{

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

intwidth, 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 содержал собственные дополнительные поля, они бы размещались в объекте после полей обоих базовых классов:

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

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

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

Button * pButton = & ib;

ImageControl * pControl = & ib;

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

Наши рекомендации