2013-06-25 4 views
1

Моя цель для данного проекта - искать и анализировать определенный последовательный пакет. Хорошей новостью является то, что уже существует типичный класс пакетов, который обрабатывает большую часть тяжелого подъема. Однако я хотел бы улучшить производительность класса, как показано ниже. Пожалуйста, простите меня, если некоторые из синтаксиса, немного смещенных, я никогда не был хорошо запоминают C++ синтаксис из памяти ... :(C++ Inheritance/Class Design Issue

class GenericPacket { 
public: 
    GenericPacket(); // does nothing except initialize member variables 
    ~GenericPacket(); 
    GenericPacket(const GenericPacket& other); 
    GenericPacket& operator=(const GenericPacket& other); 
    GenericPacket(std::queue<uint8_t>* data_stream, pthread_mutex_t* the_lock); 
    Parse(std::queue<uint8_t>* data_stream, pthread_mutex_t* the_lock); 
    // "get" functions go here... 
    // ... 

protected: 
    // the functions below are called by Parse() 
    ParseHeader(std::queue<uint8_t>* data_stream, pthread_mutex_t* the_lock); 
    ParseData(std::queue<uint8_t>* data_stream, pthread_mutex_t* the_lock); 
    ParseCheckSum(std::queue<uint8_t>* data_stream, pthread_mutex_t* the_lock); 

private: 
    // member variables go here... 

Основная функция класса является чтение в потоке данных, который был в очереди и обрабатывать его в различных компонентах пакета, а также проверяет, что пакет, который он удалил из очереди для достоверности. Есть еще два конструктора и соответствующие функции «Анализ», которые связаны с случаями, когда вызывающий абонент не модифицирует очередь во время построения, а также другую версию, которая использует простой массив вместо очереди.Однако это всего лишь обертки вокруг вызова функции Parse(), показанного выше. Также обратите внимание, что вызывающий может использовать конструктор по умолчанию и вызывать Parse вручную , или вызовите конструктор, отличный от стандартного, который будет pt, чтобы сделать объект «полезным», заполнив переменные-члены данными из первого проанализированного пакета. Также обратите внимание, что этот класс ничего не делает для декодирования данных, которые он находит в вызове ParseData. Он просто хранит шестнадцатеричный шестнадцатеричный массив в массиве uint8_t.

Теперь, с этой информацией об этом, у меня есть текущая проблема поиска особо специфического пакета, на который приходится MAYBE 2% всего трафика. Кроме того, я хочу иметь возможность декодировать данные, которые добавят больше переменных-членов. Что-то вроде этого:

class HighlySpecificPacket { 
public: 
    HighlySpecificPacket(); 
    // non-default constructor that calls parse 
    HighlySpecificPacket(std::queue<uint8_t>* data_stream, pthread_mutex_t* the_lock, int* status); 
    ~HighlySpecificPacket(); 
    // copy constructor and the like... 
    // ... 
    Parse(std::queue<uint8_t>* data_stream, pthread_mutex_t* the_lock, int* status); 
    // ... 
private: 
    double real_data; 
    // etc... 

В основном, функция Анализировать() в классе HighlySpecificPacket могут использовать многие из защищенных функций в GenericPacket. Он может использовать эти функции, но прекратить обработку после ParseHeader(), когда он отмечает, что у пакета нет подписи пакета, который я специально ищу. Кроме того, статус int * можно использовать для того, чтобы сообщать вызывающему абоненту количество байтов, которое может быть эффективно проигнорировано (таким образом, сохраняя поток производителя от ненужного нажатия в очередь). Кроме того, (я должен был упомянуть об этом ранее), защищенные функции в GenericPacket должны и должны быть отвлечены от пользователя, так как они всегда вызывается Parse() GenericPacket.

В целом, я потерял лучший способ двигаться вперед. Я не хочу, чтобы все функции GenericPacket были подвержены клиенту (а именно, небезобезопасным конструкторам), но я могу повторно использовать много кода через наследование из-за защищенных функций в базовом классе. У меня есть возможность изменять класс GenericPacket по мере необходимости. Для меня это, безусловно, тоже «есть-а», но я теряюсь относительно механизмов того, как этого добиться. Я хотел бы использовать частное наследство, но мне неоднократно говорили, что это плохая пратица и только исправление для группы.

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

  1. Что лучший способ обойти это? Если я использую композицию, я теряю доступ к функциям, которые я хочу использовать повторно. Но использование частного наследования, как известно, является взломанным, тем более что мне придется вручную писать функции-обертки, чтобы разоблачить части базового класса, которые я хочу использовать клиенту (а именно, функции «getter») ...

  2. Есть ли способ предотвратить dynamic_casts или перехватить их? В этом случае смысл перехода от производного класса к базовому классу. Однако приведение из базового класса в производный класс должно происходить только в том случае, если заголовок соответствует сигнатуре для моего конкретного пакета.

  3. Имеет ли смысл включать конструктор, который принимает базовый класс в качестве аргумента для производного класса? Есть специальное название для этого? Вот что я думаю о: DerivedClass & DerivedClass (const BaseClass & base); По сути, я бы проверял подпись заголовка, а затем только полную конструкцию в том случае, если подпись заголовка соответствовала случаю конкретного пакета.

  4. Теперь, когда я открыл банку червей, спросив о кастинге и конструкторах от базы до производной ... как насчет операторов равенства/неравенства/присваивания и т. Д.? Должен ли я писать конкретные случаи каждого из них, чтобы проверить производную и базу? Например, я могу вернуться «истина», если все элементы базового класса были такими же, когда делают что-то вроде:

    если (база == получена)

    А что-то вроде:

    derived = base; // take in all elements from base and attempt to construct it as a specific case of the base class 
    
  5. Мне даже нужно беспокоиться о том, что оператор-конструктор копирования/присваивания для производного класса, если все, что у него есть, - double/ints/etc. (без указателей/динамической памяти)? Базовый класс имеет конструктор копирования и оператор присваивания из-за динамически распределенной памяти. Разве не созданный конструктор копии по умолчанию просто вызывает конструктор базовой копии?

Спасибо за помощь. Я знаю, что мой пост много, чтобы принять участие, поэтому я ценю терпение. Я думаю, что я наткнулся на первый «реальный» случай, чтобы я использовал наследование помимо примеров Shape, Rectangle, Square, Circle и т. Д., Которые я узнал в школе. Еще раз спасибо.

Edited добавить для ясности:

Вот почему я хочу доступ к ParseHeader() и т.д. Функции:

В GenericPacket:

Parse(std::queue<uint8_t>* data_stream, pthread_mutex_t* the_lock) { 
    ParseHeader(data_stream, the_lock); 
    ParseData(data_stream, the_lock); // generic, only an array of hex 
    ParseCRC(data_stream, the_lock); //determines validity 
} 

В HighlySpecificPacket:

Parse(std::queue<uint8_t>* data_stream, pthread_mutex_t* the_lock, int* status) { 
    ParseHeader(data_stream, the_lock); 
    // do checks here to see if the packet is actually the kind I want 
    if (header_ == WHAT_I_WANT) { 
    ParseData(data_stream, the_lock); 
    ParseCRC(data_stream, the_lock); 
    } else { 
    *status = header_.packet_length_; // number of bytes to ignore. 
    } 
} 
+0

Хороший вопрос. Вы уверены, что вам нужны ** указатели и ссылки не будут выполнять эту работу? –

+0

Руководства по стилю моей команды не нравятся неконстантным указателям, а я AM messing с материалом, поэтому я использую указатели в результате. –

+0

«Я бы хотел использовать личное наследование» - Почему? Не секрет, что HighlySpecificPacket является своего рода GenericPacket. См. Http://stackoverflow.com/a/656235/544557 –

ответ

0
class GenericPacket 
{ 
public: 
    static std::shared_ptr<GenericPacket> fromStream(std::queue<uint8_t>* data_stream, pthread_mutex_t* the_lock) 
    { 
     // extract the header and return std::make_shared<MySpecificPacket>(...) where you've chosen MySpecificPacket accordingly 
    } 

    bool parse(std::queue<uint8_t>* data_stream, pthread_mutex_t* the_lock) 
    { 
     return this->parse_(data_stream, the_lock); 
    } 

protected: 
    // override this method for specific implementations of parse, can also be made abstract if there is no default behaviour 
    virtual bool parse_(std::queue<uint8_t>* data_stream, pthread_mutex_t* the_lock); 
}; 

class MySpecificPacket: public GenericPacket 
{ 
protected: 
    bool parse_(std::queue<uint8_t>* data_stream, pthread_mutex_t* the_lock) 
    { 
     // do something specific, including decoding the data if you need to 
    } 
}; 

И как клиент (производитель) класс использует это:

std::shared<GenericPacket> packet = GenericPacket::fromStream(stream, lock); 
if (packet->parse(stream, lock)) 
{ 
    // push onto event queue... 
} 
0

Благодаря отличной дискуссии в комментариях, здесь предлагается предлагаемое решение что я рассматриваю реализации, что просто ударил меня, пока я был в душе (как в сторону, кто-то еще найти, что лучшее мышление делается в душе?):

namespace packets { 
ParseHeader(...); 
ParseData(...); 
ParseCheckSum(...); 

class GenericPacket { 
    // same stuff as before, except the scope of the "protected" functions... 
}; 

class HighlySpecificPacket { 
    // same stuff as before... 
    // new stuff: 
public: 
    // will probably have to add wrappers here to expose 
    // some GenericPacket member variables... 
private: 
    GenericPacket packet; // composition for all the necessary member variables 

Я думаю, что это хороший способ выставлять только те вещи, которые я хочу для клиента, за исключением того факта, что теперь я должен дать им прямой доступ к функциям ParseHeader/ParseData/ParseChecksum, чего я надеюсь избежать (и именно поэтому они были изначально защищены. Кроме того, я все еще могу использовать композицию, если я делаю эти функции общедоступными, но моя проблема остается в том, что я хочу позволить HighlySpecificPacket иметь доступ к этим функциям, не позволяя пользователю HighlySpecificPacket иметь к ним доступ.

Плюсы этого подхода состоят в том, что он облегчит многое из dynamic_cast и приравнивает один тип к другим проблемам, которые у меня были ...