Polymorphi c вызов виртуальных функций с различным количеством аргументов - PullRequest
0 голосов
/ 08 января 2020

Можно ли добиться поведения, продемонстрированного ниже, с помощью виртуальных функций? И если это не правильный путь к go о полиморфизме, то что будет правильным в этом примере?

class Base_
{
    float x;
    float y;
    float z;
public:
    Base_(float xx=0, float yy=0, float zz=0)
    {
        x = xx;
        y = yy;
        z = zz;
    }
   virtual void SetParemeters(what here?)=0; //Different number of arguments 
};

class Derived_1 :public Base_
{
    float r;
public:
    Derived_1(float rr=1, float xx=0, float yy=0, float zz=0):Base_(xx,yy,zz)
    {
        r=rr;
    }
    virtual void SetParemeters(float v1) //Different number of arguments
    {
        r=v1;
    }
};

class Derived_2 :public Base_
{
    float k;
    float w;
public:
    Derived_2(float kk=1, float ww=1,float xx=0, float yy=0, float zz=0):Base_(xx,yy,zz)
    {
        k=kk;
        w=ww;
    }
    virtual void SetParemeters(float v1, float v2) //Different number of arguments
    {
        k=v1;
        w=v2;
    }
};
int main()
{
    Derived_1 d1;
    Derived_2 d2;
    Base_ *ptr;

    ptr = &d1;
    ptr -> SetParemeters(one argument)

    ptr = &d2;
    ptr-> SetParemeters(one or two arguments) 

    return 0;
}

И даже если мне удастся добиться этого, как я могу установить только второй параметр ( k) здесь: ptr-> SetParemeters(one or two arguments)?

Я искал ответы, но нашел только ответы на конкретный c сценарий ios, что затруднило для меня понимание всего этого.

Ответы [ 3 ]

0 голосов
/ 08 января 2020
Derived_1 d1;
Derived_2 d2;
Base_ *ptr;

ptr = &d1;
ptr->SetParameters(one argument)

ptr = &d2;
ptr->SetParameters(one or two arguments) 

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

  • во время первого вызова SetParameters(), ptr указывает на объект типа Derived_1
  • при втором вызове указывает на объект типа Derived_2.

Это, в свою очередь, означает, что вам известны типы stati c - - и на самом деле вам нужно из-за разных подписей.

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

Итак, если вы обладаете этими знаниями во время компиляции, просто используйте вызовы не виртуальных методов .


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

class Car : public Vehicle
{
    virtual void addFuel(const Petrol& f) override;
};

class Airplane : public Vehicle
{
    virtual void addFuel(const Kerosene& f) override;
};

Как бы тогда выглядела базовая функция?

class Vehicle
{ 
    virtual ~Vehicle() {} // don't forget this!
    virtual void addFuel(const /* which type? */& f) = 0;
};

Один из вариантов - сделать тип топлива иерархией (и Kerosene, и Petrol наследуют класс Fuel):

class Vehicle
{ 
    virtual ~Vehicle() {}
    virtual void addFuel(const Fuel& f) = 0;
};

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

class Airplane : public Vehicle
{
    virtual void addFuel(const Fuel& f) override
    {
        if (auto* k = dynamic_cast<const Kerosene*>(&f))
        {
            // use concrete fuel
        }
    }
};
0 голосов
/ 08 января 2020

Вы можете сделать SetParameters функцию variadi c и сделать интерфейс polymorphi c внутренним, предоставляя записываемые параметры в форме c (здесь в виде вектора указателей на них):

class Base_
{
    float x;
    float y;
    float z;
public:
    Base_(float xx=0, float yy=0, float zz=0)
    {
        x = xx;
        y = yy;
        z = zz;
    }

    virtual std::vector<float*> getAllExtraParameters() = 0;

    template<class ... Ts>
    void SetExtraParameters(Ts&& ... ts)
    {
        auto extras = getAllExtraParameters();
        if (sizeof...(ts) > extras.size())
          throw std::runtime_error("Too many parameters given!");

        // Fold expression - could be implemented differently in C++ < 17.
        int index = 0;
        ((*extras[index++] = ts), ...);
    }
};


class Derived_1 :public Base_
{
    float r;
public:
    Derived_1(float rr=1, float xx=0, float yy=0, float zz=0):Base_(xx,yy,zz)
    {
        r=rr;
    }

    std::vector<float*> getAllExtraParameters() override
    {
        return { &r };
    }
};

class Derived_2 :public Base_
{
public:
    float k;
    float w;

    Derived_2(float kk=1, float ww=1,float xx=0, float yy=0, float zz=0):Base_(xx,yy,zz)
    {
        k=kk;
        w=ww;
    }

    std::vector<float*> getAllExtraParameters() override
    {
        return { &k, &w };
    }
};

Демонстрация и тесты: https://godbolt.org/z/ofXnuH

0 голосов
/ 08 января 2020

Да, make Base_::SetParameters принимает два (необязательных) аргумента:

class Base_
{
// [...]
public:
   virtual void SetParemeters(float=0f, float=0f)=0;
};

Derived_1::SetParameters просто игнорирует первый:

class Derived_1 :public Base_
{
    // [...]
    virtual void SetParemeters(float v1, float=0f)
    {
        r=v1;
    }
};

, а Derived_2 принимает оба из них

class Derived_2 :public Base_
{
    // [...]
    virtual void SetParemeters(float v1, float v2)
    {
        k=v1;
        w=v2;
    }
};

демо: https://coliru.stacked-crooked.com/a/c528ffff005df5b9

Обратите внимание, что это значительно снижает интерес к виртуальным функциям ...

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