Лучшие практики с менеджером объектов - PullRequest
1 голос
/ 26 февраля 2010

Я немного новичок в программировании на С ++ и пишу менеджер объектов для многопользовательской игры, но у меня есть некоторые сомнения относительно того, как управлять объектами клиента. Объект client состоит из параметров подключенного клиента (таких как 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 не будет явно вызываться в основной программе (так как это статический класс). Опять же, если я говорю что-то не так, пожалуйста, скажите мне!
3) Извините за мой плохой английский:)

Ответы [ 2 ]

3 голосов
/ 26 февраля 2010

Пара комментариев:

  • Используйте std :: vector для хранения ваших клиентских объектов, а не какую-то доморощенную структуру. Это облегчит вашу жизнь
  • Я думаю, что все в порядке, если get () возвращает ссылку, при условии, что пользователь знает, что ссылка может быть признана недействительной, если он слишком долго держится за нее
  • Я бы выбросил исключение в get(), если элемента там нет; это то, для чего нужны исключения, и это позволит вам обработать недостающий элемент на соответствующем уровне

Что касается (2), вы можете обработать соответствующий сигнал и вызвать dispose (). Я думаю, что вы, вероятно, хотите закрыть сокеты перед выходом.

1 голос
/ 26 февраля 2010

Создание большого количества статических членов не превращает их в «статический класс». Это просто класс, как и любой другой, со статическими функциями-членами. В любом случае вам не нужно этого делать: просто сделайте ClientManager обычным классом и создайте один из них в своей игре.

Я рекомендую использовать std :: vector вместо собственного массива. Это сделает ваш код более надежным и упростит настройку клиентов, используя соответствующий конструктор для каждого, вместо того, чтобы использовать какой-то конструктор по умолчанию для большинства из них.

Вам не следует беспокоиться о фрагментации памяти - это настолько эзотерическая низкоуровневая детализация, что вам даже не следует об этом думать. Вероятность того, что это станет для вас проблемой, астрономически мала. Но даже если бы это было проблемой, мы не могли бы диагностировать это, основываясь на том, что вы разместили здесь, потому что мы не знаем, из чего состоит класс Client. Нет смысла иметь тщательно управляемый пул в классе ClientManager, если сам класс Client ссылается на все виды памяти в другом месте. На этом этапе вам следует сконцентрироваться на написании надежного и правильного кода, а затем оптимизировать его при необходимости.

Вы также можете вернуть указатель из ClientManager :: get. Просто убедитесь, что вы проверили, является ли оно нулевым, прежде чем его использовать. Вы можете полностью устранить необходимость возврата клиентов из ClientManager, если вы выберете другой интерфейс, например. передача операции do_something вместе с данными в член ClientManager, который будет искать клиента и вызывать операцию, если клиент был найден, или сообщать об ошибке, если это не так. (Хотя нет веских причин, по которым он не будет найден, если ваш ClientManager правильный.)

...