简体   繁体   English

对象管理器的最佳实践

[英]Best practices with object manager

I'm kinda new to c++ programming and I'm coding an object manager to a multiplayer game but I'm having some doubts about how to manage the client objects. 我是c ++编程的新手,我正在编写一个多人游戏的对象管理器,但我对如何管理客户端对象有一些疑问。 The client object is composed of connected client parameters (like IP, connected time, received data etc). 客户端对象由连接的客户端参数(如IP,连接时间,接收数据等)组成。

In order to avoid memory fragmentation, I'm planning to allocate a object pool with the max number of client allowed. 为了避免内存碎片,我打算分配一个允许客户端最大数量的对象池。 For that, I'm coding a client object manager like this: 为此,我正在编写一个像这样的客户端对象管理器:

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

Note that this class will be called only the static way so there are no constructors/destructors. 请注意,此类将仅以静态方式调用,因此没有构造函数/析构函数。 This class must be static because it is imperative that the client data can be read/modified between different types of objects. 此类必须是静态的,因为必须在不同类型的对象之间读取/修改客户端数据。

The implementation will be something like: 实现将是这样的:

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 ???????
}

Now, how do I manage the object return in the get function? 现在,如何在get函数中管理对象返回? Is it by reference the best option? 是参考最好的选择吗? I don't want to return a pointer but if it is my last option, I can live with it. 我不想返回指针,但如果它是我的最后一个选项,我可以忍受它。 I guess I can make sure that I only get registered (initialized) object in the pool, if so is this check in the get function necessary? 我想我可以确保我只在池中获得注册(初始化)对象,如果是这样,那么检查get函数是否必要? I don't want an assert because I want the code to be robust and not stop during runtime (I'm new to C++ so if I'm saying something wrong please correct me). 我不想要断言,因为我希望代码是健壮的,而不是在运行时停止(我是C ++的新手,所以如果我说错了,请纠正我)。

in the main program, I'm thinking in something like: 在主程序中,我在考虑如下:

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);
}

Am I doing it right? 我做得对吗? Thanks 谢谢

Notes: 笔记:
1) This code is on my mind and I typed it here, so I may have forgotten something. 1)这个代码在我的脑海里,我在这里打字,所以我可能忘记了一些东西。
2) The program will terminate only by being killed (I'm using linux), so the ClientManager dispose method will not be explicitly called in the main program (since it is a static class). 2)程序只会被终止(我正在使用linux)终止,因此ClientManager dispose方法不会在主程序中显式调用(因为它是一个静态类)。 Again, if I'm saying something wrong please tell me! 再说一遍,如果我说错了,请告诉我!
3) Sorry about my bad english :) 3)抱歉我的英文不好:)

Couple of comments: 几点评论:

  • Use a std::vector to hold your client objects, not some sort of homegrown structure. 使用std :: vector来保存客户端对象,而不是某种本地结构。 It'll make your life easier 它会让你的生活更轻松
  • I think it's fine if get() returns a reference provided that the user does know that the reference can be invalidated if they hang on to it for too long 我认为如果get()返回一个引用就好了,前提是用户确实知道如果引用挂起太长时间引用就会失效
  • I'd throw an exception in get() if the element isn't there; 如果元素不存在,我会在get()抛出异常; that's what exceptions are for and it will allow you to handle the missing element at the appropriate level 这就是异常的含义,它将允许您在适当的级别处理缺少的元素

As to (2), you could handle the appropriate signal and call dispose(). 对于(2),您可以处理适当的信号并调用dispose()。 I'd think you probably do want to close the sockets before you exit. 我想你可能确实想在退出之前关闭套接字。

Making lots of the member static doesn't make it a 'static class'. 使许多成员静态不会使它成为“静态类”。 It's just a class like any other, with static member functions. 它只是一个类似于任何其他类的类,具有静态成员函数。 You don't really need to do this anyway: just make ClientManager a normal class and create a single one of them in your game. 您无论如何都不需要这样做:只需将ClientManager设置为普通类并在游戏中创建其中一个。

I second the advice to use a std::vector instead of your own array. 我建议使用std :: vector而不是你自己的数组。 This will make your code more robust and make it easier for you to set up Clients, using the proper constructor for each one instead of using some sort of default constructor for the lot of them. 这将使您的代码更加健壮,并使您更容易设置客户端,使用适当的构造函数为每个客户端,而不是使用某种默认构造函数。

You shouldn't worry about memory fragmentation - that's such an esoteric low level detail that you shouldn't even be thinking about it. 你不应该担心内存碎片 - 这是一个如此深奥的低级细节,你甚至不应该考虑它。 The chance of it being a problem for you is astronomically small. 它对你来说是一个问题的机会是天文数字很小。 But even if it was to be a problem, we couldn't diagnose it based on what you've posted here, because we don't know what makes up a Client class. 但即使它是一个问题,我们也无法根据您在此处发布的内容进行诊断,因为我们不知道什么构成了Client类。 There's no point having a carefully managed pool in the ClientManager class if the Client class itself references all sorts of memory elsewhere. 如果Client类本身引用其他地方的各种内存,那么在ClientManager类中有一个精心管理的池是没有意义的。 At this stage you should concentrate on writing robust and correct code, and optimise later if needed. 在此阶段,您应该专注于编写健壮且正确的代码,并在需要时进行优化。

You may as well return a pointer from ClientManager::get. 您也可以从ClientManager :: get返回一个指针。 Just make sure you check to see if it's null before using it. 请确保在使用之前检查它是否为null。 You can entirely remove the need to return Clients from ClientManager if you choose a different interface, eg. 如果选择不同的接口,则可以完全取消从ClientManager返回客户端的需要,例如。 passing the operation do_something along with the data into a member of the ClientManager which will look up the client and call the operation if the client was found, or report an error if it was not. 将操作do_something与数据一起传递给ClientManager的成员,该成员将查找客户端并在找到客户端时调用操作,如果不是,则报告错误。 (Although there's no good reason why it would not be found, if your ClientManager is correct.) (虽然没有充分的理由说明为什么找不到它,如果你的ClientManager是正确的。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM