这个一般概念会被认为是“不好的”吗?使用函数typedef来预先计算哪个函数可以更好地处理存储的数据的概念?还是我应该坚持if和switch语句,以防止其他程序员畏缩?除了强调此示例中的名称之外:
#ifndef __PROJECT_CIMAGE_H_
#define __PROJECT_CIMAGE_H_
#define FORMAT_RGB 0
#define FORMAT_BGR 1
typedef unsigned char ImageFormat;
class CImage
{
protected:
// image data
Components* data;
ImageFormat format;
// typedef the functions
typedef void(*lpfnDeleteRedComponentProc)();
typedef void(*lpfnDeleteGreenComponentProc)();
typedef void(*lpfnDeleteBlueComponentProc)();
// specify the different functions for each supported format
void DeleteRedComponentRGB();
void DeleteGreenComponentRGB();
void DeleteBlueComponentRGB();
void DeleteRedComponentBGR();
void DeleteGreenComponentBGR();
void DeleteBlueComponentBGR();
// Add in references to which functions to use.
lpfnDeleteRedComponentProc DRC;
lpfnDeleteGreenComponentProc DGC;
lpfnDeleteBlueComponentProc DBC;
public:
Image(); // Allocate some basic data
~Image(); // Deallocate stored data
// change the image format
void SetImageFormat(ImageFormat format)
{
// shift through the data and adjust it as neccissary.
switch (format)
{
case FORMAT_RGB:
// use functions specially suited for the RGB format
DRC = DeleteRedComponentRGB;
DGC = DeleteGreenComponentRGB;
DBC = DeleteBlueComponentRGB;
break;
case FORMAT_BGR:
// use functions specially suited for the BGR format
DRC = DeleteRedComponentBGR;
DGC = DeleteGreenComponentBGR;
DBC = DeleteBlueComponentBGR;
break;
}
}
// Set's the specifyed component to 0 throughout the entire image
void DeleteRedComponent() { DRC(); }
void DeleteGreenComponent() { DGC(); }
void DeleteBlueComponent() { DBC(); }
// more, similarly pourposed, functions here...
};
#endif // __PROJECT_CIMAGE_H_
最佳答案
上面的代码有很多问题。
您无用地使用#define
,而在应该使用typedef
的地方使用enum
enum class ImageFormat:unsigned char { // unsigned char optional
FORMAT_RGB, // =0 optional
FORMAT_BGR // =1 optional
};
其次,您有一个
virtual
行为簇,您想一并交换。这怎么不给您尖叫接口类?struct ChannelSpecific {
virtual void DeleteGreen( CImage* ) = 0;
virtual void DeleteBlue( CImage* ) = 0;
virtual void DeleteRed( CImage* ) = 0;
// etc
};
template< ImageFormat format >
struct ChannelSpecificImpl;
template<>
struct ChannelSpecificImpl<FORMAT_RGB>:ChannelSpecific {
void DeleteGreen( CImage* ) final { /* etc...*/ }
// etc...
};
template<>
struct ChannelSpecificImpl<FORMAT_BGR>:ChannelSpecific {
// etc...
};
调用上述
virtual
函数的开销略高于函数指针(由于vtable不太可能位于高速缓存中),但是在连续执行大量操作的情况下,您可以找到格式,并显式转换工作程序并调用final
方法,而没有函数指针或虚拟表的开销(最多并且包括允许方法内联)。第二个优点是,您要在通道上执行的所有操作最终都变得非常统一,而与每个通道的偏移量有关。因此,通过执行以下操作,我可以消除上面的两个专业:
enum class Channel { Red, Green, Blue };
template<ImageFormat, Channel> struct channel_traits;
template<> struct channel_traits<FORMAT_RGB, Red>:std::integral_constant< size_t, 0 > {};
template<> struct channel_traits<FORMAT_RGB, Green>:std::integral_constant< size_t, 1 > {};
template<> struct channel_traits<FORMAT_RGB, Blue>:std::integral_constant< size_t, 2 > {};
template<> struct channel_traits<FORMAT_BGR, Red>:std::integral_constant< size_t, 2 > {};
template<> struct channel_traits<FORMAT_BGR, Green>:std::integral_constant< size_t, 1 > {};
template<> struct channel_traits<FORMAT_BGR, Blue>:std::integral_constant< size_t, 0 > {};
现在,我无需专门化即可编写
ChannelSpecificImpl<ImageFormat>
-我只需要访问上述traits类,就可以编写一次代码,并多次使用它。在
CImage
内部,我存储了一个ChannelSpecific
指针,该指针不包含任何数据,仅包含算法。当我换出图像格式时,ChannelSpecific
指针被换出。如果由于太多的vtable开销而发现我在使用ChannelSpecific
的方式上遇到瓶颈,那么我可以进行重构并在其中添加大型功能。如果我讨厌一直在传递
CImage
的事实,则可以在内部给ChannelSpecific
指向CImage
的指针的状态,现在代码可以使用this->cimage
来访问< cc>。另一方面,像您上面编写的代码一样,它占有一席之地。我认为它比大量的
CImage
case
语句要好。请注意,上面的一些代码是C ++ 11特定的(
switch
,带有存储说明符的enum class
,enum
),但是如果删除这些功能,该解决方案仍然可行。另请注意,您的
final
语句最终看起来像:switch (format) {
case FORMAT_RGB:
channelSpecific.reset(new ChannelSpecificImpl<FORMAT_RGB>());
case FORMAT_BGR:
channelSpecific.reset(new ChannelSpecificImpl<FORMAT_BGR>());
它几乎不需要维护,也不太可能包含错误。如果您讨厌免费商店(更具体地说,已经发现格式更改非常普遍,以至于
switch
调用对性能至关重要),请为每个目录创建一个::new
或C ++ 11 boost::variant
可能的union
。 (ChannelSpecificImpl
或std::unique_ptr<ChannelSpecific> channelSpecific
,取决于各种情况,默认情况下使用std::shared_ptr
。)最后,如果您厌倦了维护该
unique_ptr
语句(正如我倾向于的那样),则通过模板元编程进行级联的switch
魔术开关并不难-甚至是产生指针的函数指针工厂数组if
和显式数组查找以调用其中之一。 (遗憾的是,没有可变模板扩展生成实际的switch语句,但是编译器可能会将链式顺序ifs优化为相同的结构)。如果以上代码没有任何帮助,则重要的部分是您不想手动编写每个零函数。您不希望重复自己,只写一次,将孔之间的差异分解为特征类,并在格式和通道上使模板函数产生起作用的函数并编写一次。如果您不这样做,则要么必须通过宏来生成代码,否则将陷入混乱,不能通过其他方法来生成代码(并且不能调试生成器,只能调试生成的代码),或者您将仅在对您的质量检查人员会错过的某些特定渠道进行某些特定操作时才会出现一些极端情况的错误。也许不是今天,也许不是明天,但是有一天进行了更改,有人将更新升级到第18种格式特定功能,但仅限于蓝色通道。
我正在攻击一个旧的每通道映像库,该库完全按照您的建议通过这种“虚拟C样式函数指针交换”完成,并且我触摸的每个函数都使用上述技术重写。我正在大量减少代码量,提高可靠性,有时甚至提高性能。为什么?因为我能够检查常见的假设-pixelsride等于pixel pack,pixelsride等于source和dest等于-并为该情况生成了一个较不易破解的版本,而对于拐角情况又退回至一个较不易破解的版本,然后应用一口气就完成了无数不同的像素迭代代码。在现有的微优化之上使用这种微优化来维护N个不同的像素迭代代码将是昂贵的:以这种方式执行意味着我要编写一次,并获得N倍的收益。
关于c++ - 在类中广泛使用函数typedef是不好的编程习惯吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16006581/