使用不同的参数集“重载”纯虚函数

时间:2014-05-30 12:56:07

标签: 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 &kol)
{
    return RGB(0xABCDEF);
}

我完全知道为什么这段代码不起作用( RGB 中的 mixColors()没有实现纯虚函数,因为它有不同的参数集)。但是,我想问一下是否有另一种方法可以解决这个问题。让我们说我想混合颜色,但对不同的颜色类使用不同的算法。我将不胜感激任何帮助。

5 个答案:

答案 0 :(得分:3)

为什么你还需要一个虚拟方法呢?

如果参数是另一种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与不同类的颜色混合,您将在运行时获得异常。

答案 1 :(得分:3)

在继承需要子类型的语言中,例如C ++,您不能在派生类中使成员函数参数“更具体”。 (至少没有breaking type safety。)有关完整的技术说明,see here

要更具体地理解这一点,请注意,在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
};

使用重载而不是虚拟成员函数有一个缺点,但是:必须在编译时解析重载,而可以动态调度虚拟成员函数。遗憾的是,如果你需要对你想要混合的颜色执行运行时调度,那么你就有点紧张,因为C ++没有像Haskell的type classes或Common Lisp multi methods那样的东西。您可以使用visitor pattern对多个调度进行编码,但这显然不是很好。

答案 2 :(得分:2)

在这种情况下,您不需要纯虚函数。纯虚函数是函数的概念,无论如何都必须明确地重写。你想要的是一个普通的虚拟功能。

您希望传达一种颜色可以随时混合的概念。但是,您不希望保证始终实现此类功能。最好用普通的虚函数表示,它抛出异常:

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,HSV和HSL。当然它们可以相互混合,但是现在我只实现了从HSL到HSV的转换成员函数,反之亦然。

现在我可以这样:

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
}

答案 3 :(得分:1)

RGB需要能够与任何颜色混合。继承Color强加了对RGB的要求。

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

答案 4 :(得分:1)

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

以下是例子:

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