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
}