c++ - 在类中广泛使用函数typedef是不好的编程习惯吗?

标签 c++ function typedef

这个一般概念会被认为是“不好的”吗?使用函数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 classenum),但是如果删除这些功能,该解决方案仍然可行。

另请注意,您的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。 (ChannelSpecificImplstd::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/

相关文章:

python - 在 Python 中保存数据,然后在 C++ 中读取

c++ - RSA解密产生错误结果

php - 用变量的名称定义函数?

c++ - 这个声明是什么意思?

c - c中同一结构的多个名称

c++ - 没有错误,但运行时没有任何反应 (C++)

c++ - 使用 setw 和 setfill 输出格式化

Python Numpy nan_to_num 帮助

c - typedef - 不命名类型

即使 IF 语句为 false,字符指针数组也会被覆盖