对象管理器的最佳实践
我对 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)抱歉我的英语不好:)
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. The client object is composed of connected client parameters (like IP, connected time, received data etc).
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? 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? 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).
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.
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). Again, if I'm saying something wrong please tell me!
3) Sorry about my bad english :)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
一些评论:
对于 (2),您可以处理适当的信号并调用 dispose()。我认为您可能确实想在退出之前关闭套接字。
Couple of comments:
get()
if the element isn't there; that's what exceptions are for and it will allow you to handle the missing element at the appropriate levelAs to (2), you could handle the appropriate signal and call dispose(). I'd think you probably do want to close the sockets before you exit.
将大量成员设为静态并不会使其成为“静态类”。它只是一个像其他类一样的类,具有静态成员函数。无论如何,您实际上并不需要这样做:只需将 ClientManager 设为普通类并在您的游戏中创建其中一个即可。
我同意使用 std::vector 而不是您自己的数组的建议。这将使您的代码更加健壮,并使您更容易设置客户端,为每个客户端使用正确的构造函数,而不是为大量客户端使用某种默认构造函数。
您不应该担心内存碎片 - 这是一个深奥的低级细节,您甚至不应该考虑它。它对你来说成为问题的可能性非常小。但即使这是一个问题,我们也无法根据您在此处发布的内容来诊断它,因为我们不知道 Client 类是由什么组成的。如果 Client 类本身在其他地方引用各种内存,那么在 ClientManager 类中仔细管理池是没有意义的。在此阶段,您应该集中精力编写健壮且正确的代码,并在需要时进行优化。
您也可以从 ClientManager::get 返回一个指针。只需确保在使用它之前检查它是否为空。如果您选择不同的接口,例如,您可以完全消除从 ClientManager 返回客户端的需要。将操作 do_something 与数据一起传递到 ClientManager 的成员中,该成员将查找客户端并在找到客户端时调用该操作,如果没有找到则报告错误。 (尽管没有充分的理由为什么找不到它,如果您的 ClientManager 是正确的。)
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.
I second the advice to use a std::vector instead of your own array. 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. There's no point having a carefully managed pool in the ClientManager class if the Client class itself references all sorts of memory elsewhere. 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. Just make sure you check to see if it's null before using it. You can entirely remove the need to return Clients from ClientManager if you choose a different interface, eg. 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. (Although there's no good reason why it would not be found, if your ClientManager is correct.)