LINUX.ORG.RU

[OOP][C++] Отделение интерфейса от реализации

 ,


1

3

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

Кое-где предлагают решать эту проблему при помощи класса-обертки. Применяется ли этот подход в крупных проектах и есть ли более красивое решение?

Вот пример такого подхода:

// rectangle_impl.h
class RectangleImpl {
    int x, y;
public:
    RectangleImpl(void);
    void set_values (int,int);
    int area (void);
};


// rectangle.h
class RectangleImpl;

class Rectangle {
public:
    Rectangle(void);
    void set_values (int,int);
    int area (void);
private:
    RectangleImpl *impl;
};


// rectangle.cpp
//...

Rectangle::Rectangle() {
    impl = new RectangleImpl;
}

Rectangle::set_values(int w, int h) {
    impl->set_values(w, h);
}

//...
Это сильно упрощенный пример тут нет конструктора копирования, перегруженного оператора присваивания и деструктора.

Спасибо за внимание.

★★★

Я очень часто такое встречал :) Не то что бы это «класс обертка», скорее что-то вроде абстракции более высокго уровня(или объекта-контейнера).

Jetty ★★★★★
()

Кое-где предлагают решать эту проблему при помощи класса-обертки. Применяется ли этот подход в крупных проектах и есть ли более красивое решение?

Pimpl? Применяется, и очень часто. Большинством плюсовых либ. Но это не единственный способ решения проблемы.

Иногда встречается такое решение:

// rectangle.h

class ARectangle {
public:
    static ARectangle* get();
    virtual void set_values (int,int) = 0;
    virtual int area (void) = 0;
};

// rectangle.cpp
class CRectangle:public ARectangle {
    int x, y;
public:
    CRectangle(void);
    void set_values (int,int);
    int area (void);
};

ARectangle* ARectangle::get() {
    return new CRectangle;
}

void CRectangle::set_values(int w, int h)
{
    //...
}
int CRectangle::area()
{
    //...
}

//...
Какое выбрать - это вечный спор «наследование vs аггрегирование».

Deleted
()

Скрыть реализацию интерфейса, так что ее изменение не повлечет перекомпиляции клиентского кода, можно как минимум двумя способами:
1. С помощь фабрики, которая возвращает указатель на интерфейс (о каких-либо деталях реализации интерфейса знает только фабрика)
2. pimpl - http://en.wikipedia.org/wiki/Opaque_pointer

Manhunt ★★★★★
()
Ответ на: комментарий от Manhunt

franchukroman продемонстрировал первый подход, ТС - второй подход. Оба широко используются.

Manhunt ★★★★★
()

просмотрев заголовочный файл того или иного класса можно хоть и немного, но понять внутреннее устройство класса

Тебе жалко, что ли? Пусть понимают, главное, чтобы не поганили внутренние структуры. А для этого есть слово private.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

Тебе жалко, что ли? Пусть понимают, главное, чтобы не поганили внутренние структуры. А для этого есть слово private.

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

schizoid ★★★
()

Этот подход называется приватная имплементация или Pimpl и широко используется, например, в Qt.

KblCb ★★★★★
()
Ответ на: комментарий от schizoid

Ну и хорошо, можно сходить попить кофе.

Miguel ★★★★★
()
Ответ на: комментарий от schizoid

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

Тут нужно выбирать: или инлайн функций и высокая скорость работы, или отсутствие пересборки после изменений. Для C++ скорость рантайма - важнее.

Manhunt ★★★★★
()
Ответ на: комментарий от Deleted

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

frozenix ★★★
() автор топика

Спасибо большое за ответы очень позновательно получилось.

frozenix ★★★
() автор топика
Ответ на: комментарий от Manhunt

А почему нельзя все сразу? Например использовать эти приемы при разработке, а в готовом приложении использовать классы напрямую?

frozenix ★★★
() автор топика

И последний вопрос: как я понял ccache тут сильно не поможет, или я ошибаюсь?

frozenix ★★★
() автор топика

да, применяется очень часто, называется pimpl, только вот RectangleImpl пишется сразу в cpp файле без всяких инклудников.

Reset ★★★★★
()

Тред не читал. Про pimpl уже говорили?

DELIRIUM ☆☆☆☆☆
()
Ответ на: комментарий от frozenix

Мой вариант тоже имеет минусы:

1. Нельзя создавать статические объекты ARectangle

2. Нет конструктора ARectangle

А иногда эти вещи очень нужны.

Deleted
()
Ответ на: комментарий от Deleted

// rectangle.h

class ARectangle {

Где виртуальный деструктор?

2. Нет конструктора ARectangle

Зачем интерфейсу конструктор?

kondor ★★★
()
Ответ на: комментарий от kondor

Где виртуальный деструктор?

Протупил вчера. Он обязательно должен быть, иначе получим ту еще бяку.

Зачем интерфейсу конструктор?

Незачем. Более того, он невозможен. Это я сравниваю Pimpl vs наследование для отделения интерфейса от реализации.

Deleted
()

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

m0rph ★★★★★
()
Ответ на: комментарий от Deleted

Мой вариант тоже имеет минусы:

1. Нельзя создавать статические объекты ARectangle

2. Нет конструктора ARectangle

Фабрика играет роль конструктора. Статический объект не создашь, но можно создать статический unique_ptr. Или так тоже не пойдёт?

const86 ★★★★★
()
Ответ на: комментарий от const86

Фабрика играет роль конструктора.

Фабрика зафиксирует тип используемой памяти. Это не заменяет конструктор вообще ни разу.

anonymous
()
Ответ на: комментарий от anonymous

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

const86 ★★★★★
()
Ответ на: комментарий от Gorthauer

Угу, при кросс-платформенной разработке вполне удобный вариант.

m0rph ★★★★★
()
Ответ на: комментарий от Gorthauer

Не всегда, у меня бывает, что скрытый pimpl класс в разных случаях разную реализацию имеет.

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

Pavval ★★★★★
()
Ответ на: комментарий от Deleted
ARectangle* ARectangle::get() {
    return new CRectangle;
}

facepalm, sorry. не удержался.

asaw ★★★★★
()
Ответ на: комментарий от Pavval

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

Gorthauer ★★★★★
()

Не секрет, что С++ не очень хорошо отделяет интерфейс от реализации, и просмотрев заголовочный файл того или иного класса можно хоть и немного, но понять внутреннее устройство класса

а ты не просматривай

Harald ★★★★★
()
Ответ на: комментарий от Harald

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

frozenix ★★★
() автор топика

Не секрет, что С++ не очень хорошо отделяет интерфейс от реализации

Спасибо, посмеялся.

anonymous
()
Ответ на: комментарий от schizoid

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

И какого же размера у вас проекты, что это имеет значение?

gods-little-toy ★★★
()
Ответ на: комментарий от Miguel

Какие помехи могут быть в C++?

Смотря с какой стороны посмотреть. Мне, например, порой не хватает виртуальных вызовов в конструкторе.

yoghurt ★★★★★
()
Ответ на: комментарий от gods-little-toy

Около двухсот тысяч строк, ~50 модулей .so. Это так необычно?

schizoid ★★★
()
Ответ на: комментарий от yoghurt

Мне, например, порой не хватает виртуальных вызовов в конструкторе.

Направляешь значит ружье в ногу, нажимаешь на курок - осечка! Ну что за незадача...

Pavval ★★★★★
()
Ответ на: комментарий от Pavval

Не всё, что выстреливает в ногу в плюсах, есть некорректно в других языках ;)

yoghurt ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.