简体   繁体   English

对具有不同数量参数的虚函数的多态调用

[英]Polymorphic call to virtual functions with varying number of arguments

Is it possible to achieve behaviour demonstrated below with virtual functions?是否可以使用虚函数实现下面演示的行为?
And if it's not the correct way to go about polymorphism then what would be the correct way in this example?如果这不是处理多态的正确方法,那么在这个例子中正确的方法是什么?

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;
}

And even if I managed to achieve that, how can I set only second parameter (k) here:即使我设法实现了这一点,我怎么能在这里只设置第二个参数 (k):
ptr-> SetParemeters(one or two arguments) ? ptr-> SetParemeters(one or two arguments)

I searched for answers but I only found answers to specific scenarios which made the whole thing difficult for me to understand.我搜索了答案,但我只找到了特定场景的答案,这让我难以理解整件事。

Yes, make Base_::SetParameters takes two (optional) arguments:是的,make Base_::SetParameters需要两个(可选)参数:

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

Derived_1::SetParameters just ignores the first one: Derived_1::SetParameters只是忽略第一个:

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

while Derived_2 takes the both of themDerived_2需要他们两个

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

demo: https://coliru.stacked-crooked.com/a/c528ffff005df5b9演示: https : //coliru.stacked-crooked.com/a/c528ffff005df5b9

Note though, this significantly reduces the interest of virtual functions...但请注意,这显着降低了虚函数的兴趣......

Derived_1 d1;
Derived_2 d2;
Base_ *ptr;

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

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

The way how this code is written implies that you have knowledge about two things:这段代码的编写方式意味着您了解两件事:

  • at the time of the first call to SetParameters() , ptr points to an object of type Derived_1在第一次调用SetParameters()ptr指向Derived_1类型的对象
  • at the second call, it points to an object of type Derived_2 .在第二次调用时,它指向Derived_2类型的对象。

This in turn means that you know the static types -- and in fact, you need to , due to the different signatures.这反过来意味着您知道静态类型——事实上,由于不同的签名,您需要知道。

For such a scenario, dynamic polymorphism is not the right choice, because its premise is that you can talk to different implementations (overridden methods) using a uniform access (calling the virtual base method).对于这种情况,动态多态不是正确的选择,因为它的前提是您可以使用统一访问(调用虚拟基方法)与不同的实现(覆盖的方法)对话。

So, if you have this knowledge at compile time, simply use non-virtual method calls.因此,如果您在编译时掌握了这些知识,只需使用非虚拟方法调用即可。


However, there are similar scenarios where you may actually be interested in supporting different signatures at runtime , for example if you load configuration dynamically.但是,在类似的情况下,您可能实际上对在运行时支持不同的签名感兴趣,例如,如果您动态加载配置。 Here is an example, where not the number , but the types of arguments differ.这是一个示例,其中不是number ,而是参数类型不同。

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

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

How would then the base function look?那么基函数看起来如何呢?

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

One option is to make the fuel type a hierarchy as well (both Kerosene and Petrol inherit the Fuel class):一种选择是使燃料类型也成为一个层次结构( KerosenePetrol都继承了Fuel类):

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

However, in this case, each implementation would need to either rely on the fact it's passed the right fuel, or check the type at runtime.但是,在这种情况下,每个实现都需要依赖于它传递了正确的燃料这一事实,或者在运行时检查类型。

class Airplane : public Vehicle
{
    virtual void addFuel(const Fuel& f) override
    {
        if (auto* k = dynamic_cast<const Kerosene*>(&f))
        {
            // use concrete fuel
        }
    }
};

You can make SetParameters a variadic function and have the polymorphic interface be internal, providing the writeable parameters in a generic form (here as a vector of pointers to them):您可以使SetParameters成为可变参数函数,并将多态接口设为内部,以通用形式提供可写参数(此处作为指向它们的指针向量):

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 };
    }
};

Demo and tests: https://godbolt.org/z/ofXnuH演示和测试: https : //godbolt.org/z/ofXnuH

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM