C++ &引用;超载“;具有不同参数集的纯虚函数

C++ &引用;超载“;具有不同参数集的纯虚函数,c++,inheritance,virtual,C++,Inheritance,Virtual,考虑下面的代码示例 #include <iostream> using namespace std; class Color { public: virtual void mixColors(Color &anotherColor) = 0; }; class RGB : public Color { public: void mixColors(RGB &anotherColor); }; void RGB::mixColors(RGB &am

考虑下面的代码示例

#include <iostream>
using namespace std;

class Color
{
public:
    virtual void mixColors(Color &anotherColor) = 0;
};

class RGB : public Color
{
public:
    void mixColors(RGB &anotherColor);
};

void RGB::mixColors(RGB &kol)
{
    return RGB(0xABCDEF);
}
#包括
使用名称空间std;
类颜色
{
公众:
虚拟空间混合颜色(颜色和其他颜色)=0;
};
RGB类:公共颜色
{
公众:
void mixColors(RGB和其他颜色);
};
无效RGB::混合颜色(RGB和kol)
{
返回RGB(0xABCDEF);
}

我完全知道为什么这段代码不起作用(RGB中的mixColors()没有实现纯虚函数,因为它有不同的参数集)。然而,我想问,是否有其他方法来解决这个问题。假设我想混合颜色,但对不同的颜色类使用不同的算法。我非常感谢您的帮助。

您为什么需要虚拟方法

如果混合一种
RGB
颜色只有在参数是另一种RGB颜色时才有意义,那么为什么要有生成的
mixColor(color)
方法呢

如果确实需要,可以覆盖并执行动态强制转换:

class RGB : public Color
{
public:
    void mixColors(RGB &anotherColor);
    void mixColors(Color &c) override { return mixColors(dynamic_cast<RGB&>(c)); }
};

void RGB::mixColors(RGB &kol)
{
    return RGB(0xABCDEF);
}
RGB类:公共颜色 { 公众: void mixColors(RGB和其他颜色); void mixColors(Color&c)重写{return mixColors(dynamic_cast(c));} }; 无效RGB::混合颜色(RGB和kol) { 返回RGB(0xABCDEF); }
这样,如果您尝试将RGB与不同类别的颜色混合,则在运行时会出现异常。

RGB必须能够与任何颜色混合。继承颜色会对RGB施加该要求


如果RGB想要添加一种特殊的方式来混合特殊种类的颜色,那么除了一般的方式外,它还必须这样做。您可以实现两个函数并让编译器在编译时进行选择,也可以只实现所需的函数并在运行时测试其参数的类型。后者的代码可以是
if(dynamic_cast(&anotherColor)){…}else{…}

您还可以将混合分离到一个混合器类中,并通过实现转换构造函数定义不同颜色类型之间的转换。那些不能相互转换的颜色(它们不支持转换构造函数)不能混合,这会导致编译时错误

下面是一个例子:

#include<iostream>

class Color {};

class CMYK;

class RGB : public Color 

{ 
    public: 
        RGB() = default; 
        RGB(const RGB&) = default;

        RGB(const CMYK& c)
        {

        }
};


class CMYK : public Color 
{ 
    public:

        CMYK() = default; 
        CMYK(const CMYK&) = default;

        CMYK(const RGB& r)
        {
        }
};


class colorMixer
{
    public: 

        RGB mixColors(RGB r1, RGB r2)
        {
            std::cout << "mixing RGB , RGB" << std::endl;
            return RGB(); 
        }

        CMYK mixColors(CMYK c1, CMYK c2)
        {
            std::cout << "mixing CMYK, CMYK" << std::endl;
            return CMYK();
        }

        CMYK mixColors(CMYK c1, RGB r1)
        {
            // Convert RGB to CMYK
            CMYK c2(r1);

            return mixColors(c1, c2);
        }

        RGB mixColors(RGB r1, CMYK c1)
        {
            // Convert CMYK to rgb. 
            RGB r2(c1);  
            return mixColors(r1, r2);

        }
};

int main(int argc, const char *argv[])
{
    CMYK c1,c2; 
    RGB r1,r2; 

    colorMixer m1;

    m1.mixColors(c1,c2); 
    m1.mixColors(c1,r1); 
    m1.mixColors(r1,c1); 
    m1.mixColors(r1,r2); 

    return 0;
}
#包括
类颜色{};
CMYK类;
RGB类:公共颜色
{ 
公众:
RGB()=默认值;
RGB(常量RGB&)=默认值;
RGB(康斯特CMYK&c)
{
}
};
CMYK类:公共颜色
{ 
公众:
CMYK()=默认值;
CMYK(constcmyk&)=默认值;
CMYK(恒RGB&r)
{
}
};
类混色器
{
公众:
RGB混合颜色(RGB r1、RGB r2)
{

STD::CUT

在继承继承子类型的语言中,例如C++,不能在派生类中使成员函数参数“更具体”(至少不存在)。 要更具体地理解这一点,请注意,在

Color
类中,您断言存在一个带有签名的成员函数:

virtual void mixColors(Color &anotherColor) = 0;
这意味着任何颜色都可以与任何其他颜色(不一定是同一类别)混合,而这种混合程序的具体实施只取决于第一种颜色的类别,这是完全错误的

解决问题的最简单方法是简单地使用函数重载:

// I am assuming RGB and CMYK are cheap to pass by value, which seems reasonable.
// If this is not true, you can always pass them by const reference.

RGB mix_colors(RGB rgb1, RGB rgb2) { ... }

CMYK mix_colors(CMYK cmyk1, CMYK cmyk2) { ... }
或者,假设您真的想改变其中一种颜色,而不是生成新的颜色对象:

class RGB
{
    // ...
public:
    RGB & mix_colors(RGB);  // return *this at the end
};

class CMYK
{
    // ...
public:
    CMYK & mix_colors(CMYK);  // return *this at the end
};

<> P>使用重载代替虚拟成员函数有一个缺点:然而,在编译时必须重载重载,而虚拟成员函数可以动态调度。遗憾的是,如果需要对要混合的颜色执行运行时调度,那么就有点扭曲了,因为C++没有Haskell或通用的任何东西。Lisp的。您可以使用对多个分派进行编码,但这显然不太好。

在这种情况下,您不需要纯虚拟函数。纯虚拟函数是必须显式重写的函数的概念,无论发生什么情况。您需要的是普通虚拟函数

您希望传达这样一个概念,即颜色总是可以混合的。但是,您不希望保证这样的函数总是可以实现。这最好用引发异常的正常虚拟函数来表示:

class Color
{
public:
    virtual ~Color(void) = 0;
    virtual auto mix_colors(Color& color) -> Color&
    {
        throw std::logic_error("Cannot mix unrelated color schemes");
    }
};
Color::~Color(void)
{ /* */ }
如果您完全不喜欢异常,您可以打印到
std::cerr
或类似的文件。您也可以编写自己的异常并捕获它

现在,您可以创建不同的配色方案并重载此继承的虚拟成员函数:

class HSL; class HSV; class RGB;
class RGB: public Color
{ /* */ };
class HSV: public Color
{
public:
    auto mix_colors(HSL& color) -> HSV&
    {
        std::cout << "Mixing HSV with HSL" << '\n';
        return *new HSV{};
    }
};
class HSL: public Color
{
public:
    auto mix_colors(HSV& color) -> HSL&
    {
        std::cout << "Mixing HSL with HSV" << '\n';
        return *new HSL{};
    }
};

为什么不能使签名与基类虚拟函数的签名相同?…并且要根据颜色或RGB做出不同的行为,请使用另一个虚拟继承方法doAlgoBla(),或类似getType()的方法,甚至Sfinae。我不太明白类型类如何解决此问题(尽管这是多种方法的自然应用)。您仍然可以在类型类中使用名义类型和单一分派,不是吗?@MatthieuM。如果所有涉及的颜色必须具有相同的类型:
class Color c=>Mix c,其中{Mix::c->c->c}
。如果颜色可能具有不同的类型:
类(颜色a、颜色b、颜色c)=>mixabc | ab->c其中{Mix::a->b->c}
。考虑到多参数类型类和函数依赖性,类型类的表达能力是多方法的严格超集。啊,我还没有听说过多参数类型类。有趣的:)
int main(void)
{
    RGB rgb{};
    HSV hsv{};
    HSL hsl{};

    HSV new_hsv{hsv.mix_colors(hsl)};  // Mixing HSV with HSL
    HSL new_hsl{hsl.mix_colors(hsv)};  // Mixing HSL with HSV
    RGB new_rgb{rgb.mix_colors(hsv)};  // terminating with uncaught exception of type std::logic_error: Cannot mix unrelated color schemes Abort trap: 6
}