C++ 几种特定方法还是一种通用方法?

C++ 几种特定方法还是一种通用方法?,c++,C++,这是我在这个奇妙的网页上查阅了很长时间后的第一个问题 也许我的问题有点傻,但我想知道其他人对此的看法。更好的做法是,创建几个特定的方法,或者只创建一个泛型方法?这里有一个例子 unsigned char *Method1(CommandTypeEnum command, ParamsCommand1Struct *params) { if(params == NULL) return NULL; // Construct a string (command) with those specif

这是我在这个奇妙的网页上查阅了很长时间后的第一个问题

也许我的问题有点傻,但我想知道其他人对此的看法。更好的做法是,创建几个特定的方法,或者只创建一个泛型方法?这里有一个例子

unsigned char *Method1(CommandTypeEnum command, ParamsCommand1Struct *params)
{
if(params == NULL) return NULL;

// Construct a string (command) with those specific params (params->element1, ...)

return buffer; // buffer is a member of the class 
}

unsigned char *Method2(CommandTypeEnum command, ParamsCommand2Struct *params)
{
...
}

unsigned char *Method3(CommandTypeEnum command, ParamsCommand3Struct *params)
{
...
}
unsigned char *Method4(CommandTypeEnum command, ParamsCommand4Struct *params)
{
...
}

我不喜欢后一种选择的主要原因是

ParamsCommand1Struct *value = (ParamsCommand1Struct *) params;
因为“params”不能是指向“ParamsCommand1Struct”的指针,而是指向“ParamsCommand2Struct”或其他人的指针


我真的很感激你的意见

首先,您需要决定使用哪种语言。在这里用
C
C++
标记问题毫无意义。我假设C++。< /P> 如果您可以创建一个通用函数,那么这当然更可取(为什么您更喜欢多个冗余函数?);你能?但是,您似乎不知道模板。我们需要查看您在此处忽略的内容,以确定模板是否合适:

//用那些特定的参数(参数->元素1,…)构造一个字符串(命令)

在一般情况下,假设模板是合适的,所有这些都会变成:

template <typename T>
unsigned char *Method(CommandTypeEnum command, T *params) {
    // more here
}
模板
无符号字符*方法(CommandTypeEnum命令,T*params){
//更多
}


另一方面,如何声明
缓冲区
?是否返回指向动态分配内存的指针?喜欢RAII类型对象,避免动态分配内存,如果是这样。

< P>如果您使用C++,那么我将避免使用Value*,因为您并不需要。拥有多种方法没有什么错。请注意,在第一组示例中,您实际上不必重命名函数-您可以使用不同的参数重载函数,这样每种类型都有一个单独的函数签名。归根结底,这类问题是非常主观的,有很多种方法。查看第一种类型的函数,您可以通过查看模板函数的使用来获得更好的服务,您可以创建一个结构。这就是我用来处理控制台命令的方法

typedef int       (* pFunPrintf)(const char*,...);
typedef void      (CommandClass::*pKeyFunc)(char *,pFunPrintf);

struct KeyCommand
{
    const char * cmd;
    unsigned char cmdLen;
    pKeyFunc pfun;
    const char * Note;
    long ID;
};

#define CMD_FORMAT(a) a,(sizeof(a)-1)
static KeyCommand Commands[]=
{
    {CMD_FORMAT("one"),            &CommandClass::CommandOne,               "String Parameter",0},
    {CMD_FORMAT("two"),            &CommandClass::CommandTwo,               "String Parameter",1},
    {CMD_FORMAT("three"),          &CommandClass::CommandThree,             "String Parameter",2},
    {CMD_FORMAT("four"),           &CommandClass::CommandFour,              "String Parameter",3},

}; 

#define  AllCommands sizeof(Commands)/sizeof(KeyCommand)
和解析器函数

void CommandClass::ParseCmd( char* Argcommand )
 {
            unsigned int x;
            for ( x=0;x<AllCommands;x++)
            {
                if(!memcmp(Commands[x].cmd,Argcommand,Commands[x].cmdLen ))
                {
                    (this->*Commands[x].pfun)(&Argcommand[Commands[x].cmdLen],&::printf);
                    break;
                }
            }

            if(x==AllCommands)
            {
                // Unknown command
            }

}
void CommandClass::ParseCmd(char*Argcommand)
{
无符号整数x;
对于(x=0;x*命令[x].pfun)(&Argcommand[Commands[x].cmdLen],&::printf);
打破
}
}
if(x==AllCommands)
{
//未知命令
}
}

我使用线程安全的Primtf pPrtuf,所以忽略它。

< P>我不知道你想做什么,但是在C++中,你可能应该从这样的格式化程序基类派生出多个类:

class Formatter
{
    virtual void Format(unsigned char* buffer, Command command) const = 0;
};

class YourClass
{
public:
    void Method(Command command, const Formatter& formatter)
    {
        formatter.Format(buffer, command);
    }

private:
    unsigned char* buffer_;
};

int main()
{
    //
    Params1Formatter formatter(/*...*/);
    YourClass yourObject;

    yourObject.Method(CommandA, formatter);
    // ...
}

这将从类中移除处理所有params内容的责任,并使其成为一个函数。如果在进一步的开发过程中会有新的命令或参数,您不必修改(并最终破坏)现有代码,只需添加实现新功能的新类。

一般答案

在本文中,Steve Macguire的建议是在特定情况下使用不同的函数(方法)。原因是您可以断言与特定情况相关的条件,并且您可以更轻松地进行调试,因为您有更多的上下文

一个有趣的例子是用于动态内存分配的标准C运行时函数。大部分都是多余的,因为
realloc
实际上可以做(几乎)你需要的一切。如果您有
realloc
,则不需要
malloc
free
。但是当您有这样一个通用函数,用于几种不同类型的操作时,很难添加有用的断言,很难编写单元测试,也很难看到调试时发生了什么。Macquire进一步提出,不仅应该
realloc
只做重新分配,还应该有两个不同的功能:一个用于增长块,一个用于收缩块

虽然我大体上同意他的逻辑,但有时使用一种通用方法(通常是在操作高度数据驱动的情况下)具有实际优势。因此,我通常会根据具体情况做出决定,倾向于创建非常具体的方法,而不是过于通用的方法

具体答案

在您的情况下,我认为您需要找到一种方法,从细节中找出通用代码。
开关
通常是一个信号,表明您应该使用带有虚拟函数的小型类层次结构


如果您喜欢单一方法方法,那么它可能只是更具体方法的调度器。换句话说,switch语句中的每种情况都只需调用相应的
Method1
Method2
,等等。如果希望用户只看到通用方法,则可以将特定实现设为私有方法。

通常,最好提供单独的函数,因为它们通过原型名称和参数直接和可见地向用户传达可用的信息;这也导致了更直接的文档

我使用多用途函数的一次是用于类似query()函数的功能,其中许多次要的查询函数(而不是导致函数的激增)被捆绑到一个函数中,并带有一个通用的输入和输出void指针


一般来说,考虑一下您正试图通过API原型本身与API用户通信的内容;清楚地了解API可以做什么。他不需要太多的细节;他确实需要知道核心函数,这些函数是API的核心所在。

虽然没有完整的答案,但这应该指导您正确的方向:一个函数一个责任。更喜欢只负责一件事并且做得很好的代码
class Formatter
{
    virtual void Format(unsigned char* buffer, Command command) const = 0;
};

class YourClass
{
public:
    void Method(Command command, const Formatter& formatter)
    {
        formatter.Format(buffer, command);
    }

private:
    unsigned char* buffer_;
};

int main()
{
    //
    Params1Formatter formatter(/*...*/);
    YourClass yourObject;

    yourObject.Method(CommandA, formatter);
    // ...
}