2014-09-25 2 views
0

Недавно я хотел получить C++ для динамического решения члена/функции по его входному параметру, который входит в некоторые производные версии. Вот что я имею в виду:C++ Динамическое связывание в аргументе метода

#include <iostream> 

class Base { 
}; 

class DerivedA : public Base { 
}; 

class DerivedB : public Base { 
}; 

class DerivedC : public Base { 
}; 

class Test { 
public: 
    void world(DerivedA *instance) 
    { 
     std::cout << "DerivedA" << std::endl; 
    } 

    void world(DerivedB *instance) 
    { 
     std::cout << "DerivedB" << std::endl; 
    } 

    void world(Base *instance) 
    { 
     std::cout << "Base" << std::endl; 
    } 
}; 

int main() 
{ 
    Base *a = new Base; 
    Base *b = new DerivedA; 
    Base *c = new DerivedB; 
    Base *d = new DerivedC; 

    Test hello; 
    hello.world(a); 
    hello.world(b); 
    hello.world(c); 
    hello.world(d); 

    return 0; 
} 

Поведение Я ХОТЕЛ это:

Base 
DerivedA 
DerivedB 
Base 

Но наверняка на выходе я действительно получаю это:

Base 
Base 
Base 
Base 

Я понимаю, что динамическое связывание является другим способом, разрешая правильную функцию-член в производных классах базы, а не как этот путь, но может ли он работать для этого?

Может быть, я только отсутствует важный момент ..

Однако многие заранее спасибо!

Sebastian

+0

Какой здесь вариант использования? Я не понимаю, почему вы не можете просто определить каждую переменную как соответствующий производный тип вместо 'Base'. Единственный способ получить нужную функциональность - использовать 'dynamic_cast <>'. –

+2

Вы передаете 'Base *' всем этим функциям. Вам нужно использовать [виртуальные функции] (http://www.learncpp.com/cpp-tutorial/122-virtual-functions/) для того, что вы хотите достичь (вам нужно было бы сделать функцию 'world' членом- функция). – jliv902

+0

@RedAlert: вам также потребуется иметь хотя бы одну виртуальную функцию-член и выполнить 'if (dynamic_cast (экземпляр) {...}' - оба из них, вероятно, более трудоемкие, чем вызов виртуальной функции в первое место. –

ответ

3

Тип a, b, c и d все Base*. Компилятор не отслеживает «что содержит переменная». Если это то, что вы хотите сделать, то вам необходимо использовать виртуальную функцию в классе, который вы черпаете из, например:

class Base { 
public: 
    virtual const char* MyName() { return "Base"; } 
}; 

class DerivedA : public Base { 
public: 
    virtual const char* MyName() { return "DerivedA"; } 
}; 
... similiar for all derived classes ... 

void world(Base *instance) 
{ 
    std::cout << instance->MyName() << std::endl; 
} 

(Edit: Для того, чтобы получить именно поведение вы перечислить в первом случае, вам 'd необходимо НЕ реализовать функцию MyName() в классе DerivedC)

Таким образом, использование класса-обертки может быть решением для тестовой установки. Вот кое-что я просто изрубил, без особого рассмотрения и сложности:

#include <iostream> 

class Base { 
}; 

class DerivedA : public Base { 
}; 

class DerivedB : public Base { 
}; 

class DerivedC : public Base { 
}; 

class Test { 
public: 
    void world(DerivedA *instance) 
    { 
     std::cout << "DerivedA" << std::endl; 
    } 

    void world(DerivedB *instance) 
    { 
     std::cout << "DerivedB" << std::endl; 
    } 

    void world(Base *instance) 
    { 
     std::cout << "Base" << std::endl; 
    } 
}; 

template<typename T> 
class Wrapper 
{ 
public: 
    Wrapper(T* i) : instance(i) 
    { 
    } 
    ~Wrapper() 
    { 
     delete instance; 
    } 

    void doTest(Test& t) 
    { 
     t.world(instance); 
    } 

    T* instance; 
}; 


int main() 
{ 
    Test hello; 
    Wrapper<Base> a(new Base); 
    Wrapper<DerivedA> b(new DerivedA); 
    Wrapper<DerivedB> c(new DerivedB); 
    Wrapper<DerivedC> d(new DerivedC); 

    a.doTest(hello); 
    b.doTest(hello); 
    c.doTest(hello); 
    d.doTest(hello); 

    return 0; 
} 
+1

В C++ вы не можете этого сделать (ну, по крайней мере, не без использования 'if (dynamic_cast <...> (...)) {...}' конструкций типа, и это НЕ ТОЛЬКО не правильный путь). –

+0

Я общаюсь с вами в этом, мне также не нравится 'if (dynamic_cast ...' thing ... –

+0

Просто отличный подход с оболочкой. Также показывает пределы C++;) Не уверен, использую ли я это или принять его как подсказку для использования более правильного дизайна OO :) - Спасибо! –

1

В вашем примере вы не имеете времени выполнения схемы полиморфизма (то есть динамическое связывание). У вас есть перегруженная функция-член и при разрешении перегрузки компилятор правильно выбирает void world(Base *instance).

Для того, чтобы получить то, что вы хотите, вы должны применить схему наследования, как показано ниже:

class Base { 
public: 
    virtual ~Base() {} 
    virtual void world() const { std::cout << "Base" << std::endl; } 
}; 

class DerivedA : public Base { 
public: 
    virtual ~DerivedA() {} 
    void world() const { std::cout << "DerivedA" << std::endl; } 
}; 

class DerivedB : public Base { 
public: 
    virtual ~DerivedB() {} 
    void world() const { std::cout << "DerivedB" << std::endl; } 
}; 

class DerivedC : public Base { 
public: 
    virtual ~DerivedC() {} 
    using Base::world; 
}; 

Live Demo

Edit:

Для того, чтобы сохранить свой код в intented в одном месте вы могли бы добавить к приведенной выше схеме следующую измененную версию класса Test:

class Test { 
public: 
    void world(DerivedA *instance) { instance->world(); } 
    void world(DerivedB *instance) { instance->world(); } 
    void world(Base  *instance) { instance->world(); } 
}; 

Live Demo

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

+0

спасибо, но как Я прокомментировал решение Mats Peterssons, я хотел сохранить код внутри «Test» -Class. –

 Смежные вопросы

  • Нет связанных вопросов^_^