C++中的多态枚举

8

我有以下枚举声明:

enum MessageType{  
    REQ_LOGIN,
    REQ_GET_FIELD,       

    RES_LOGIN,
    RES_GET_FIELD
}

enum Request{
    REQ_LOGIN,
    REQ_GET_FIELD
};

enum Respond{
    RES_LOGIN,
    RES_GET_FIELD
};

显然我在枚举中重复了元素。有没有什么方法可以防止这种情况发生?

编辑: 我在一个通用类上使用“MessageType”来通过网络发送它,另一边我解析这个类的对象并分发消息。但是我有不同的客户端;有些只希望使用“Request”类型成员的对象,有些只希望使用“Response”类型成员的对象。

使用“Message”类,我正在创建“DispatcherRequest”。

class Message
{
public:
……….
    MessageType messageType;
}


struct DispatcherRequest
{
..........
    Request type;
};

1
我觉得你需要详细说明这些枚举代表什么。现在它没有意义,例如为什么MessageType是另外两个的联合? - Skurmedel
谢谢,现在我明白了 :) 另一个问题,请求和响应是否总是共享成员(只有前缀改变)? - Skurmedel
@Skurmedel,我不确定你所说的“共享成员”具体是什么意思,但每个请求都有响应。 - metdos
@metdos:如果“Request”有“REQ_LOGIN”,那么“Respond”一定也有“RES_LOGIN”吗? - Mooing Duck
9个回答

8
为什么不尝试像这样做呢?
enum MainType{  
    REQUEST,
    RESPONSE
};

enum SubType{
    LOGIN,
    GET_FIELD
};

class Message {
   MainType type;
   SubType sub_type;
   ...
};

+1. 我认为这是一个不错的解决方案,但也许加入一些信息可以进一步完善它。 - Skurmedel
+1. 虽然它不是我确切需要的,但这是一个创新的解决方案。 - metdos
你可以将这个答案与Thomas的答案结合起来。在Message类中定义enum MainType,并在RequestResponse类中定义enum SubType。将SubType sub_typeMessage移动到RequestResponse中。这样每种消息类型都可以有自己独特的子类型(只有合法的子类型)。然后代码中的枚举值看起来像Message::REQUESTResponse::GET_FIELD,这是尽可能易读的。 - Mike DeSimone

4

如果不知道这个设计背后的想法,很难说出来,但是您可以考虑更多面向对象的方法。例如:

class Message {
    public:
        virtual void send() = 0;
};

class Request : public Message {
    public:
        virtual void send();
}

class Response : public Message {
    public:
        virtual void send();
}

抱歉,我也没有看到你的观点。也许如果你更详细地阐述一下设计背后的思路,展示一下这些枚举是如何建模以及代码的使用方式,会更好理解。 - Thomas
1
我认为你在两个地方忘记了写 : public Message - Mike DeSimone
@metdos:既然您已经有了一个名为 Message 的类,为什么不能从中继承这两个子类并删除 messageType 字段呢? - Thomas
如果您想摆脱 messageType,您可以在 Message 中用 virtual int getMessageType() = 0; 替换它,在 Request 中用 virtual int getMessageType() { return REQUEST; } 进行替换。 我不知道哪种方法会更好;我个人认为 messageType 会更好一些。很遗憾 C++ 不允许您在 Message 中使用 virtual static const int messageType = 0;,在 Request 中使用 virtual static const int messageType = REQUEST;,换句话说,不能让您将常量存储在虚函数表中而不是对象本身... ^_- - Mike DeSimone

2
如果我对PeterK的回答的评论不够清晰,这里是最终的代码:
class Message {
public:
    enum MainType {  
        REQUEST,
        RESPONSE
    };
    Message(MainType type_): type(type_) {}
    virtual void send() = 0;
private:
    MainType type;
};

class Request: public Message {
public:
    enum SubType {
        LOGIN,
        GET_FIELD
    };
    Request(SubType sub_type_): Message(Message::REQUEST), 
        sub_type(sub_type_) {}
    virtual void send();
private:
    SubType sub_type;
};

class Response: public Message {
public:
    enum SubType {
        LOGIN,
        GET_FIELD
    };
    Response(SubType sub_type_): Message(Message::RESPONSE), 
        sub_type(sub_type_) {}
    virtual void send();
private:
    SubType sub_type;
};

2
你提到了多态枚举,为什么不只使用一个枚举并将其命名为你计划命名的基础枚举,比如 "消息类型"?这样可以避免重复元素。

我不希望客户知道一般枚举的其他元素。 - metdos
我理解你的观点,但从发布的代码来看,你试图将客户端与其他元素隔离可能有些过度。按照编码方式,如果更改任一枚举,则必须重新编译客户端。此外,在客户端方面,他们不一定需要“了解”其他枚举值。他们可以忽略他们不关心的值。 - nathan

0

Java中的Spy(抱歉,这是草稿视图):

class MessageType
{  
protected:
    MessageType(int value);//visible for descending classes only
    MessageType(const MessageType& other);
public:
    static const MessageType REQ_LOGIN, //assign later with integer value
    REQ_GET_FIELD,       

    RES_LOGIN,
   RES_GET_FIELD;
}

clas Request : public MessageType
{
};

clas Respond : public MessageType
{
};

0
在您的代码示例中,枚举请求(Request)和响应(Response)的值相同(REQ_LOGIN 和 RES_LOGIN 的值均为0,REQ_GET_FIELD 和 RES_GET_FIELD 的值均为1),并且它们的值与枚举消息类型(MessageType)中的值不符(REQ_LOGIN 的值为0,REQ_GET_FIELD 的值为1,RES_LOGIN 的值为2,RES_GET_FIELD 的值为3)。这是不是一个问题呢?
如果您想要枚举的数字保持一致,您可以尝试以下方法:
enum MessageCategories
{
Request = 0,
Response,
AnythingElse
}
const int Watermark = 100;

枚举类型MessageCategories和常量整数Watermark是所有类共通的。 现在您可以像下面这样重新定义您的枚举:

enum Request
{
REQ_LOGIN = MessageCategories::Request * Watermark,
REQ_GET_FIELD,
REQ_LAST_ITEM,
}
enum Response
{
RES_LOGIN = MessageCategories::Response * Watermark,
RES_GET_FIELD,
RES_LAST_ITEM,
}

在这种情况下,您不需要使用枚举消息类型(MessageType),因为所有的枚举代码都是一致的。

是的,这是一个问题,但在你的解决方案中,我该如何使用 void sendMessage(MessageType mes); 函数?你建议在函数头中使用 "int" 替代枚举吗? - metdos
是的,在处理所有枚举值的通用函数中,您必须使用“int”而不是枚举。但是,您仍然可以针对特定函数使用特定的枚举。 - Haspemulator

0
也许是因为没有使用枚举类型?
我总觉得 C++ 的枚举类型限制太多了,无法满足我的需求。
class MessageType
{
public:
  virtual ~MessageType();
  bool operator==(MessageType const& rhs) const;
  bool operator!=(MessageType const& rhs) const;
protected:
  MessageType(const char* type);
private:
  const char* mType;
};

class RequestType: public MessageType
{
public:
  static RequestType const Login() { return RequestType("Login"); }
  static RequestType const GetField { return RequestType("GetField"); }
protected:
  RequestType(const char* type);
};

// same for ResponseType

这里有多态行为,您可以限制客户端:

void someServerFunc(MessageType const& type);

void someClientFunc(RequestType const& type);

塔达姆!


0
为什么你不这样做呢:
void sendMessage(Request); void sendMessage(Respond);
简单的重载?

Message类中的MessageType成员会发生什么? - metdos

0
也许我在这个问题的回答中提供的方法更适合您的设计目标。为了清晰起见,这里是适应您问题的代码。
typedef struct{
    enum {
        LOGIN,
        GET_FIELD
    };
}MessageType;

typedef struct : public MessageType {
    //this struct inherits the fields of MessageType,
    //and can be accessed in code like so, Request::LOGIN or Request::GET_FIELD

    //omit this enum declaration if you do not wish to extend the base enum
    enum {
        //additional fields here
    };
}Request;

typedef struct : public MessageType {
    enum {
        //additional fields here
    };
}Response;

到目前为止,我遇到的唯一警告是可以使用 == != 直接将 Request 类型和 Response 类型的字段进行比较,而不管这两个类型是否不同。

如果在C++11中实现强类型枚举,情况可能并非如此,但我的编译器不支持该功能,因此无法测试。

希望这有所帮助。干杯!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接