【问题标题】:Best practices with object manager对象管理器的最佳实践
【发布时间】:2010-02-25 21:11:46
【问题描述】:

我对 C++ 编程有点陌生,我正在为多人游戏编写对象管理器,但我对如何管理客户端对象有些疑问。客户端对象由连接的客户端参数(如 IP、连接时间、接收数据等)组成。

为了避免内存碎片,我打算分配一个允许最大客户端数量的对象池。为此,我正在编写这样的客户端对象管理器:

ClientManager.h

#include "Client.h"

class ClientManager { 
public: 
static void init(int max); //initialize pool (allocate memory) 
static void dispose(); //dispose pool (deallocate memory) 
static bool add(int socketFd); //add connected client by its socket file descriptor 
static bool remove(int socketFd); //remove connected client by its socket fd
static Client& get(int socketFd); //get the client object by its socket fd

private: 
Client* clientList; //array of allocated clients objects
int maxClient; //max number of connected clients allowed

请注意,此类将仅以静态方式调用,因此没有构造函数/析构函数。这个类必须是静态的,因为客户端数据必须可以在不同类型的对象之间读取/修改。

实现将类似于:

ClientManager.cpp

void ClientManager::init(int max) {
maxClient = max;
clientList = new Client[maxClient];
}

void ClientManager::dispose() {
maxClient = 0;
delete [] clientList;
clientList = NULL;
}

bool ClientManager::add(int socketFd) {
//search pool for non-initialized object
//if(there is a non-initializes object) { initialize it with socketFd and return true}
//else return false;
}

bool ClientManager::remove(int socketFd) {
//search pool for socketFd
//if(socketFd found) { clear object (make it non-initialized) and return true}
//else return false
}

Client& ClientManager::get(int socketFd) {
//search for object position
if(pos) return clientList[pos];
else ???????
}

现在,如何管理 get 函数中的对象返回?参考是最好的选择吗?我不想返回一个指针,但如果这是我最后的选择,我可以接受它。我想我可以确保我只在池中获得注册(初始化)对象,如果是这样,是否需要在 get 函数中进行检查?我不想要断言,因为我希望代码健壮并且在运行时不会停止(我是 C++ 新手,所以如果我说错了,请纠正我)。

在主程序中,我的想法是:

Daemon.cpp

#include "ClientManager.h"

int main(...) {
ClientManager::init(100);

while(1) {
//do server stuff
//get onConnect event (new client is connecting)
//get onDisconnect event (connected client has gone offline)
//get onDataReceived event (connected client has sent data)
}
}

void onConnect(int socketFd) {
ClientManager::add(socketFd);
}

void onDisconnect(int socketFd) {
ClientManager::remove(socketFd);
}

void onDataReceived(int socketFd) {
do_something(ClientManager::get(socketFd).data);
}

我做得对吗?谢谢

注意事项:
1) 这段代码在我的脑海中,我在这里输入了它,所以我可能忘记了一些东西。
2)程序只有被杀死才会终止(我使用的是linux),因此在主程序中不会显式调用ClientManager的dispose方法(因为它是一个静态类)。再说一次,如果我说错了,请告诉我!
3) 对不起我的英语不好:)

【问题讨论】:

  • 为什么要担心内存碎片?如果您只有 100 个客户,那应该没什么大不了的。
  • 重复?显然是同一张海报:stackoverflow.com/questions/2320106/…
  • 是的,这个问题也是我的问题,但这个问题是关于客户经理池
  • @thebretness 100 只是一个例子
  • 担心内存碎片似乎还为时过早。

标签: c++ multiplayer


【解决方案1】:

几个cmets:

  • 使用 std::vector 来保存您的客户端对象,而不是某种本土结构。它会让您的生活更轻松
  • 我认为如果 get() 返回一个引用没问题,前提是用户确实知道如果他们坚持太久该引用可能会失效
  • 如果元素不存在,我会在get() 中抛出异常;这就是例外情况,它将允许您在适当的级别处理缺少的元素

至于 (2),您可以处理适当的信号并调用 dispose()。我认为您可能确实想在退出之前关闭套接字。

【讨论】:

  • 返回引用的问题是输入参数是否无效。我会返回一个指针,使用 NULL 作为错误情况。否则,您可以创建一个名为 nullClient 的成员 Client 并像 NULL 一样使用它,而不会出现段错误的风险(尽管您将面临更高的未检测到逻辑错误的风险)。
  • 关于向量,最初我使用它,但为了在对象之间传递客户端数据,我必须传递一个指针(如 stackoverflow.com/questions/2320106/… 中所述)。所以我想静态对象管理器会更好
  • 另外,你能给我一个地方,我可以学习如何使用你提到的信号吗?
  • 如果关注运行时效率,请在init 中使用vector::reserve 来减少分配开销。
【解决方案2】:

将大量成员设为静态并不能使其成为“静态类”。它只是一个与其他类一样的类,具有静态成员函数。无论如何,您实际上并不需要这样做:只需将 ClientManager 设为一个普通类并在您的游戏中创建一个。

我赞同使用 std::vector 而不是您自己的数组的建议。这将使您的代码更加健壮,并使您更容易设置客户端,为每个客户端使用正确的构造函数,而不是为它们使用某种默认构造函数。

您不应该担心内存碎片 - 这是一个如此深奥的低级细节,您甚至不应该考虑它。它对你来说是个问题的可能性很小。但即使这是一个问题,我们也无法根据您在此处发布的内容进行诊断,因为我们不知道什么构成了 Client 类。如果 Client 类本身在其他地方引用各种内存,那么在 ClientManager 类中拥有一个精心管理的池是没有意义的。在这个阶段,您应该专注于编写健壮且正确的代码,并在以后根据需要进行优化。

您也可以从 ClientManager::get 返回一个指针。只要确保在使用它之前检查它是否为空。如果您选择不同的接口,例如,您可以完全消除从 ClientManager 返回客户端的需要。将操作 do_something 与数据一起传递给 ClientManager 的成员,该成员将查找客户端并在找到客户端时调用该操作,否则报告错误。 (虽然没有充分的理由找不到它,但如果您的 ClientManager 是正确的。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-22
    • 2015-07-29
    • 2019-06-07
    相关资源
    最近更新 更多