Сохранение абстрактного значения объекта в оболочке и использование наследования - PullRequest
1 голос
/ 02 июля 2019

У меня проблемы с наследованием классов C ++.У меня есть класс, который имеет виртуальный метод, например:

class IFoo {
public:
    virtual int sayFoo() = 0;
};

И у меня есть несколько реализаций, например:

class Foo1: public IFoo {
public:
    virtual int sayFoo() {
        return 1;
    }
};

class Foo2: public IFoo {
public:
    virtual int sayFoo() {
        return 2;
    }
};

Я хочу держать IFoo экземпляр в пустышкеконтейнерный класс (как своего рода обертка), открывающий тот же интерфейс IFoo:

class DummyWrapper : public IFoo {
public:
    DummyWrapper(IFoo& foo): foo{foo} {}
    virtual int sayFoo() {
        return foo.sayFoo(); //ALPHA
    }
private:
    IFoo& foo; //BETA
};

Обычно все должно работать, например, так:

IFoo& foo = Foo1{};
DummyWrapper wrapper{foo};

wrapper.sayFoo();

Моя проблемачто foo на самом деле является просто r-значением, которое удаляется после того, как выходит его область действия, как здесь:

DummyWrapper generateWrapper() {
    return DummyWrapper{Foo1{}};
}

Это приводит к проблемам чтения в строке "ALPHA".Решением было бы поместить значение r в кучу и использовать указатели для доступа к foo.Так как я новичок в C ++ и, возможно, я попал в проблему XY, мой вопрос:

  • это единственное решение?Разве нет лучшего способа для решения проблемы?
  • Я не думаю, что смогу заменить строку "BETA" на IFoo foo, так как таким образом DummyWrapper всегда будет хранить байтыIFoo, а не IFoo реализации.Или, может быть, я могу использовать значение IFoo foo для вызова производных виртуальных методов?

Спасибо за любой ответ

Ответы [ 2 ]

4 голосов
/ 02 июля 2019

это единственное решение?Разве нет лучшего метода для решения проблемы?

Как только полиморфизм вовлекается, к сожалению, да, и нет, нет.Но вы получаете почти эквивалентное решение для хранения IFoo foo, если вы используете умный указатель:

// member:
std::unique_ptr<IFoo> foo;

// constructor:
DummyWrapper(std::unique_ptr<IFoo>&& foo): foo(std::move(foo)) { }
// need to accept r-value         ^
// reference: std::unique_ptr is only movable, not copiable!
// for the same reason, you need to preserve the r-value reference
// on assignment to member...

// creation of the wrapper:
return DummyWrapper(std::make_unique<Foo1>(/* arguments for constructor, if there are */));

Я не думаю, что смогу заменить строку "BETA" на IFoo foo, поскольку в этомТаким образом, DummyWrapper всегда будет хранить байты IFoo, а не реализации IFoo.Или, может быть, я могу использовать значение IFoo foo для вызова производных виртуальных методов?

Абсолютно правильно: этот эффект называется «разрезанием объекта», когда вы назначаете производный объект базовому, вселишний материал, поступающий из производного типа, теряется.Довольно распространенная проблема (например, когда люди пытаются сохранить производные объекты в std::vector<Base>).

1 голос
/ 02 июля 2019

Обычно мы создаем обертку, когда хотим изменить интерфейс класса или реализовать дополнительные функции, например,
std::queue<T> - это обертка / адаптер на std::std::deque<T> (по умолчанию)
Шаблон класса std::queue<T> действует как оболочка для нижележащего контейнера - предоставляется только определенный набор функций. Очередь выталкивает элементы сзади основного контейнера и выталкивает их спереди.
В вашем случае я не думаю, что вам нужно DummyWrapper, вы можете использовать IFoo вместо DummyWrapper, и он будет выполнять ту же работу.

Давайте рассмотрим следующую функцию,

bool isEven( IFoo& ifoo)  // Not const& because sayFoo() is not const method.
{
  return ( 0 == ( ifoo.sayFoo() % 2)) ;
}

Эта функция будет работать для всех типов, таких как Foo1, Foo2, и обертка не требуется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...