У меня есть два варианта одного и того же метода. У меня также есть экземпляр типа базового класса, но я не знаю, к какому конкретному классу он относится. Теперь я хочу автоматически выбирать подходящий метод в зависимости от фактического типа объекта. Это кажется невозможным, и единственное решение, которое я могу придумать, - это проверить все возможности с помощью кастинга. Однако должно быть более приятное решение.

Вот мой минимальный пример:

// Example program
#include <iostream>
#include <string>
#include <memory>

class A
{
public:    
    virtual void bar() const = 0;
};

class B : public A
{
public:
    void bar() const
    {
        std::cout << "B.bar()" << std::endl;
    }
};

class C : public A
{
public:    
    void bar() const
    {
        std::cout << "C.bar()" << std::endl;
    }    
};

class Z
{
public:
    Z(int variable) : m_variable(variable) {};

    void foo(std::shared_ptr<B> b)
    {
        std::cout << "Calling foo(B) method! " << m_variable << std::endl;
        b->bar();
    }

    void foo(std::shared_ptr<C> c)
    {
        std::cout << "Calling foo(C) method!" << m_variable << std::endl;
        c->bar();
    }
private:
  int m_variable;
};


int main()
{
  std::shared_ptr<A> b(new B());

  Z z(42);

  //z.foo(b); // This doesn't work

  // But this does
  std::shared_ptr<B> b_cast = std::dynamic_pointer_cast<B>(b);
  if (b_cast.get())
    z.foo(b_cast);
}

http://cpp.sh/9fqne

На данный момент мне приходится прибегать к dynamic_pointer_cast, но я считаю его некрасивым и не очень удобным в обслуживании.

Я также не хочу добавлять функциональность foo() к классам B и C, потому что это небольшие независимые структуры данных, с которыми работают многие другие классы.

Большое спасибо!

РЕДАКТИРОВАТЬ: В исходном посте я слишком упростил. Новый пример должен прояснить ситуацию.

0
Pfaeff 24 Июл 2017 в 16:36
3
Вы слышали о полиморфизме? Вы можете сделать их виртуальными функциями-членами и получить такое поведение.
 – 
NathanOliver
24 Июл 2017 в 16:37
1
Обратите внимание, что линия a = b; приводит к разрезанию. (Здесь нет никаких данных-членов, но, в частности, эта строка не превращает a внезапно в B.)
 – 
cdhowie
24 Июл 2017 в 16:38
1
Прочтите о полиморфизме, наследовании, интерфейсах, виртуальных функциях и указателях, чтобы назвать некоторые из них.
 – 
Ron
24 Июл 2017 в 16:40
Классы B и C предназначены только для хранения данных. Метод foo ни в коем случае не должен входить ни в один из них, поскольку он не имеет ничего общего с самими этими классами.
 – 
Pfaeff
24 Июл 2017 в 16:43
1
Похоже на шаблон посетителей ?
 – 
BoBTFish
24 Июл 2017 в 16:57

2 ответа

Добавьте чистую виртуальную функцию foo() в базовый класс и переопределите в последующих производных классах. Затем пусть ваша глобальная функция foo() (которая не имеет ничего общего с функциями-членами с тем же именем) примет ссылку на std::shared_ptr const в качестве параметра:

#include <iostream>
#include <memory>
class A{
public:
    virtual void foo() = 0;
};

class B : public A{
public:
    void foo() override{
        std::cout << "Calling foo(B) method!" << std::endl;
    }
};

class C : public A{
public:
    void foo() override{
        std::cout << "Calling foo(C) method!" << std::endl;
    }
};

void foo(const std::shared_ptr<A>& param){
    param->foo();
}

int main(){
    std::shared_ptr<A> b = std::make_shared<B>();
    std::shared_ptr<A> c = std::make_shared<C>();
    foo(b);
    foo(c);
}
1
Ron 24 Июл 2017 в 17:08
Представьте foo функцией-членом другого класса, который работает с объектами типов B и C. Так как у меня много таких классов, я не хочу добавлять все эти функции к самим B и C. Результат foo() также сильно зависит от переменных-членов реализующего класса.
 – 
Pfaeff
24 Июл 2017 в 17:14

Как указал BoBTFish, шаблон посетителя является потенциальным решением этой проблемы:

// Example program
#include <iostream>
#include <string>
#include <memory>

class B;
class C;

class Visitor
{
public:
    virtual void visit(B* b) const = 0;
    virtual void visit(C* b) const = 0;
};

class A
{
public:    
    virtual void bar() const = 0;
    virtual void accept(const Visitor* visitor) = 0;
};

class B : public A
{
public:
    void bar() const
    {
        std::cout << "B.bar()" << std::endl;
    }

    void accept(const Visitor* visitor) 
    {
        visitor->visit(this);
    }
};

class C : public A
{
public:    
    void bar() const
    {
        std::cout << "C.bar()" << std::endl;
    }

    void accept(const Visitor* visitor) 
    {
        visitor->visit(this);
    }    
};

class Z : public Visitor
{
public:
    Z(int variable) : m_variable(variable) {};

    void visit(B* b) const
    {
        std::cout << "Calling foo(B) method! " << m_variable << std::endl;
        b->bar();
    }

    void visit(C* c) const
    {
        std::cout << "Calling foo(C) method!" << m_variable << std::endl;
        c->bar();
    }
private:
  int m_variable;
};


int main()
{
  std::shared_ptr<A> b(new B());

  Z z(42);

  b->accept(&z);
}

http://cpp.sh/2vah5

Большое спасибо!

0
Pfaeff 25 Июл 2017 в 10:11