Моя цель для данного проекта - искать и анализировать определенный последовательный пакет. Хорошей новостью является то, что уже существует типичный класс пакетов, который обрабатывает большую часть тяжелого подъема. Однако я хотел бы улучшить производительность класса, как показано ниже. Пожалуйста, простите меня, если некоторые из синтаксиса, немного смещенных, я никогда не был хорошо запоминают 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 с момента последнего времени я активно использовал наследование было, когда я был в школе ...):
Что лучший способ обойти это? Если я использую композицию, я теряю доступ к функциям, которые я хочу использовать повторно. Но использование частного наследования, как известно, является взломанным, тем более что мне придется вручную писать функции-обертки, чтобы разоблачить части базового класса, которые я хочу использовать клиенту (а именно, функции «getter») ...
Есть ли способ предотвратить dynamic_casts или перехватить их? В этом случае смысл перехода от производного класса к базовому классу. Однако приведение из базового класса в производный класс должно происходить только в том случае, если заголовок соответствует сигнатуре для моего конкретного пакета.
Имеет ли смысл включать конструктор, который принимает базовый класс в качестве аргумента для производного класса? Есть специальное название для этого? Вот что я думаю о: DerivedClass & DerivedClass (const BaseClass & base); По сути, я бы проверял подпись заголовка, а затем только полную конструкцию в том случае, если подпись заголовка соответствовала случаю конкретного пакета.
Теперь, когда я открыл банку червей, спросив о кастинге и конструкторах от базы до производной ... как насчет операторов равенства/неравенства/присваивания и т. Д.? Должен ли я писать конкретные случаи каждого из них, чтобы проверить производную и базу? Например, я могу вернуться «истина», если все элементы базового класса были такими же, когда делают что-то вроде:
если (база == получена)
А что-то вроде:
derived = base; // take in all elements from base and attempt to construct it as a specific case of the base class
Мне даже нужно беспокоиться о том, что оператор-конструктор копирования/присваивания для производного класса, если все, что у него есть, - 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.
}
}
Хороший вопрос. Вы уверены, что вам нужны ** указатели и ссылки не будут выполнять эту работу? –
Руководства по стилю моей команды не нравятся неконстантным указателям, а я AM messing с материалом, поэтому я использую указатели в результате. –
«Я бы хотел использовать личное наследование» - Почему? Не секрет, что HighlySpecificPacket является своего рода GenericPacket. См. Http://stackoverflow.com/a/656235/544557 –