вызов метода c ++ для абстрактного класса, возвращаемого функцией - PullRequest
1 голос
/ 27 апреля 2020

Я пропускаю что-то очевидное, но вот моя проблема

с чистым абстрактным классом IFoo

class IFoo
{
 public:
    virtual bool isBar1() const=0;
    virtual bool isBar2() const=0;
};

и 2 реализациями

class Foo1 : public IFoo
{
 public:
    bool isBar1() const override { return true;}
    bool isBar2() const override { return false;}
};

class Foo2 : public IFoo
{
 public:
    bool isBar1() const override { return false;}
    bool isBar2() const override { return true;}
};

I есть управляющий класс, который должен вызывать правильный метод в зависимости от переменной protocol

class FooManager : public IFoo
{
 public:
    bool isBar1() const override 
    {
      switch(protocol)
      {
        case 1: return Foo1().isBar1();
        case 2: return Foo2().isBar1();
        default: return false;
      }
    }
    bool isBar2() const override
    {
      switch(protocol)
      {
        case 1: return Foo1().isBar2();
        case 2: return Foo2().isBar2();
        default: return false;
      }
    }
    void setProtocol(int proto){this->protocol = proto;}
 private:
    int protocol{0};
};

Но есть куча методов, и я не хочу помещать switch(protocol) везде, учитывая, что это действительно Повторяющийся и новый FooX может быть добавлен в любое время.

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

Я не могу просто создать метод getFoo (), который возвращает IFoo, потому что это абстрактный класс И я не могу вернуть IFoo & ни того, ни другого, потому что он вернет ссылку на временный.

IFoo& FooManager::getFoo()
{
      switch(protocol)
      {
        case 1: return Foo1();
        case 2:
        default:  return Foo2();
      }
  //return reference to temporary
}

Что еще я могу сделать?

Ответы [ 4 ]

2 голосов
/ 27 апреля 2020

Вы могли бы возвращать unique_ptr, например

std::unique_ptr<IFoo> FooManager::getFoo() {
    switch (protocol) {
        case 1: return std::make_unique<Foo1>();
        case 2: 
        default: return std::make_unique<Foo2>();
    }
}

. Это приведет к тому, что данные будут указателем и полиморфизм будет применяться при вызове функций-членов

1 голос
/ 29 апреля 2020

Благодаря ответу @UnholySheep вот что я закончил:

class FooManager : public IFoo{
  public:
     using FooFunc = bool(*)(const IFoo&);

     bool callFoo(FooFunc function) const{
        switch(protocol) {
          case 1: return function(Foo1());
          case 2: return function(Foo2());
          //and the other cases
        }
    }
    bool isBar1() const override {
       return callFoo([](const IFoo& foo){return foo.isBar1();});
    }
    bool isBar2() const override {
       return callFoo([](const IFoo& foo){return foo.isBar2();});
    }
};

мои классы FooX остаются прежними, а switch(protocol) находится в одной функции, что означает, что если приходит новый протокол, я просто нужно создать новый FooY для этого протокола и добавить его в коммутатор, чтобы он заработал. Все это с проверками времени компиляции и без использования кучи. Еще раз спасибо @UnholySheep и другим.

1 голос
/ 28 апреля 2020

Поскольку у вас очень специфическое c требование, я предлагаю очень конкретное c решение для этой конкретной проблемы (которое может не подходить в других местах). Чтобы избежать необходимости использовать динамическое распределение c и указатели или ссылки, вы можете «подделать» полиморфизм, используя указатели на функции. Небольшой пример с учетом требований, которые вы упомянули в ваших комментариях:

class Foo {
    public:
    // function pointer aliases to make them easier to use
    // I opted to make the two functions take different parameters for demonstration purposes
    using isBar1Func = bool(*)(const Foo*);
    using isBar2Func = bool(*)(int);
    // constructor requiring the function pointers as parameters
    Foo(int value, isBar1Func bar1func, isBar2Func bar2func) : 
        m_value(value), m_bar1Func(bar1func), m_bar2Func(bar2func) {}

    bool isBar1() const {
        return m_bar1Func(this);
    }

    bool isBar2() {
        return m_bar2Func(m_value);
    }

    int getValue() const {
        return m_value;
    }

    private:
       int m_value;
       isBar1Func m_bar1Func;
       isBar2Func m_bar2Func;
};

// example functions to be passed into the constructor
static bool testBar1Func(const Foo* foo) {
    return foo->getValue() != 0;
}

static bool testBar2Func(int value) {
    return value > 1;
}

// getFoo can simply return a copy
Foo FooManager::getFoo() {
    switch (protocol) {
        case 1: return Foo(1, testBar1Func, testBar2Func);
        // also works with non-capturing lambdas, which can be converted to function pointers
        case 2: return Foo(2, 
                           [](const Foo* foo) { return foo->getValue() != 1; },
                           [](int value) {return value != 12; });
        // add remaining cases as desired
    }
}
1 голос
/ 27 апреля 2020

Вы можете вернуть std::unique_ptr, чтобы получить поведение полиморфа c, но можете контролировать время жизни возвращаемого объекта.

std::unique_ptr<IFoo> FooManager::getFoo()
{
      switch(protocol)
      {
        case 1: return std::make_unique<Foo1>();
        case 2:
        default:  return std::make_unique<Foo2>();
      }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...