首页 \ 问答 \ C ++继承/类设计问题(C++ Inheritance/Class Design Issue)

C ++继承/类设计问题(C++ Inheritance/Class Design Issue)

我对给定项目的目标是查找和解析特定的串行数据包。 好消息是已经编写了一个通用的数据包类来处理大部分繁重的工作。 但是,我想改进课程的表现,如下所示。 请原谅我的一些语法略有偏差,我从来没有擅长从内存中记住C ++语法...... :(

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()函数调用的包装器。 另请注意,调用者可以使用默认构造函数并手动调用Parse,也可以调用非默认构造函数,该构造函数将通过使用第一个已解析数据包中的数据填充成员变量来尝试使对象“有用”。 另请注意,此类不会对它在ParseData调用中找到的数据进行解码。 它只是将原始十六进制存储在uint8_t数组中。

现在,有了背景信息,我目前正在寻找一个高度特定的数据包,占所有流量的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类中的Parse()函数可以利用GenericPacket中的许多受保护函数。 它可以使用这些函数,但是当它注意到数据包没有我特意寻找的数据包的签名时,在ParseHeader()之后停止处理。 此外,int * status可用于向调用者发回可有效忽略的字节数(从而将生产者线程从不必要地推送到队列中)。 另外,(我之前应该已经提到过),GenericPacket中受保护的函数应该并且需要从用户那里抽象出来,因为它们总是被GenericPacket的Parse()调用。

总的来说,我迷失了前进的最佳方式。 我不希望GenericPacket的所有功能都暴露给客户端(即非线程安全构造函数),但由于基类中受保护的函数,我可以通过继承重用大量代码。 我也可以根据需要修改GenericPacket类。 对我而言,这显然也是一种“是一种”关系,但我对如何实现这一点的机制感到迷茫。 我想使用私人继承,但我已经多次被告知这是一个糟糕的实践,只有一个创可贴。

总的来说,我被困住了,并且有以下问题(请原谅,如果这些是自从我最后一次积极使用继承以来就是在我回到学校的时候这些都是noobish ...):

  1. 解决这个问题的最佳方法是什么? 如果我使用合成,我将无法访问我想要重用的函数。 但是已知使用私有继承是hacky,特别是因为我必须手动编写包装函数来公开我希望客户端使用的基类部分(即“getter”函数)......

  2. 有没有办法阻止dynamic_casts或拦截它们? 在这种情况下,从派生类到基类的转换是有意义的。 但是,只有当标头与我的特定数据包的签名匹配时,才应该从基类转换到派生类。

  3. 包含一个构造函数是否有意义,该构造函数将基类作为派生类的参数? 这有特别的名字吗? 以下是我的想法:DerivedClass&DerivedClass(const BaseClass&base); 基本上我会检查标头签名,然后只有在标头签名与特定数据包的情况匹配的情况下才完成构造。

  4. 现在我通过询问从基础到派生的转换和构造函数来打开一堆蠕虫......等于/不等式/赋值运算符等等? 我是否必须编写每个特定案例来检查派生与基础? 例如,如果在执行以下操作时所有基类元素都相同,则可以返回“true”:

    if(base == derived)

    怎么样的:

    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。 (没有指针/动态内存)? 由于动态分配的内存,基类具有复制构造函数和赋值运算符。 默认的派生复制构造函数不是只调用基本复制构造函数吗?

谢谢你的帮助。 我知道我的帖子有很多内容,所以我很感激耐心。 我想我偶然发现了第一个“真实”的案例,除了我在学校学到的形状,矩形,方形,圆形等的例子之外,还使用了继承。 再次感谢。

编辑添加为清晰:

这就是我想要访问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.
  }
}

My objective for a given project is to look for and parse a particular serial packet. The good news is that there is a generic packet class already written that handles most of the heavy lifting. However, I'd like to improve on the performance of the class as is shown below. Please excuse me if some of the syntax is slightly off, I've never been good at remembering C++ syntax from memory... :(

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...

The class's basic function is to read in a data stream that has been queued and process it into the various components of the packet. It also checks the packet it stripped out of the queue for validity. There are also two more constructors and corresponding "Parse" functions that are associated with cases where the caller isn't modifying the queue during construction and also another version that uses a simple array instead of a queue. Both of these are just wrappers around the Parse() function call shown above. Also, note that the caller can either use the default constructor and call Parse manually, or call the non-default constructor which will attempt to make the object "useful" by filling in the member variables with the data from the first parsed packet. Also, note that this class does nothing to decode the data it finds in the ParseData call. It simply stores the raw hexadecimal in a uint8_t array.

Now, with that background info out of the way, I have the current issue of looking for a highly specific packet that accounts for MAYBE 2% of all traffic. Also, I desire to have the ability to decode the data, which will add more member variables. Something like this:

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...

Basically, the Parse() function in the HighlySpecificPacket class can leverage many of the protected functions in GenericPacket. It can use these functions, but stop processing after ParseHeader() when it notes that the packet does not have the signature of the packet I am specifically looking for. Also, int* status can be used to signal back to the caller the number of bytes that can effectively be ignored (thus saving a producer thread from needlessly pushing to the queue). Also, (I should have mentioned this earlier), the protected functions in GenericPacket should and need to be abstracted away from the user since they are always called by GenericPacket's Parse().

Overall, I'm lost as to the best way to move forward. I don't want all of the features of GenericPacket to be exposed to the client (namely, the non-thread safe constructors), yet I can reuse a good deal of code via inheritance due to the protected functions in the base class. I have the ability to modify the GenericPacket class as needed as well. To me this is clearly an "is-a" relationship as well, but I'm lost as to the mechnaisms of how to achieve this. I would like to use private inheritance, but I've been told numerous times that this is bad pratice and only a band-aid fix.

Overall, I am stuck and have the following questions (Please excuse if these are noobish since the last time I actively used inheritance was when I was back in school...):

  1. What's the best way of going about this? If I use composition, I lose access to the functions I want to reuse. But using private inheritance is known to be hacky, especially since I will have to manually write wrapper functions to expose the portions of the base class that I want the client to use (namely "getter" functions)...

  2. Is there a way to prevent dynamic_casts, or to intercept them? Casting from the derived class to the base class makes sense in this case. However, casting from the base class to the derived class should only happen if the header matches the signature for my specific packet.

  3. Would it make sense to include a constructor that takes in the base class as an argument for the derived class? Is there a special name for this? Here's what I'm thinking of: DerivedClass& DerivedClass(const BaseClass& base); Essentially I would check the header signature and then only complete construction in the event that the header signature matched the case of the specific packet.

  4. Now that I opened up a can of worms by asking about casting and constructors from the base to the derived... what about equality/inequality/assignment operators, etc? Do I have to write specific cases of each to check derived vs. base? For example, I can return "true" if all the base class elements were the same when doing something like:

    if (base==derived)

    What about something like:

    derived = base;  // take in all elements from base and attempt to construct it as a specific case of the base class
    
  5. Do I even need to worry about the copy constructor / assignment operator for the derived class if all it has is doubles/ints/etc. (no pointers/dynamic memory)? The base class has a copy constructor and assignment operator due to dynamically allocated memory. Doesn't the default derived copy constructor just call the base copy constructor?

Thanks for all the help. I know my post has alot to take in, so I appreciate the patience. I'm thinking that I stumbled across the first "real" case for me to use inheritance besides the examples of Shape, Rectangle, Square, Circle, etc. that I learned in school. Thanks again.

Edited to add for clarity:

Here's why I want access to the ParseHeader(), etc. functions:

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

In 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.
  }
}

原文:https://stackoverflow.com/questions/17290533
更新时间:2023-07-04 12:07

最满意答案

这不行。 RedirectToAction导致302浏览器无法携带复杂的模型。

有一些替代方法,您可以使用TempData来存储模型,执行重定向和检索数据。 您甚至可以直接调用EatCandy操作而不是重定向,但是自动匹配视图将无法工作,您必须在EatCandy操作结束时强制使用“EatCandy”视图名称。


This won't work. RedirectToAction results in a 302 to the browser which just is not capable of carrying complicated models.

There are some alternatives, you could use the TempData to store the model, do the redirect and retrieve the data. You could probably even call the EatCandy action directly instead of redirecting, however the auto matching the view won't work and you'd have to force the "EatCandy" view name at the end of the EatCandy action.

相关问答

更多

相关文章

更多

最新问答

更多
  • 获取MVC 4使用的DisplayMode后缀(Get the DisplayMode Suffix being used by MVC 4)
  • 如何通过引用返回对象?(How is returning an object by reference possible?)
  • 矩阵如何存储在内存中?(How are matrices stored in memory?)
  • 每个请求的Java新会话?(Java New Session For Each Request?)
  • css:浮动div中重叠的标题h1(css: overlapping headlines h1 in floated divs)
  • 无论图像如何,Caffe预测同一类(Caffe predicts same class regardless of image)
  • xcode语法颜色编码解释?(xcode syntax color coding explained?)
  • 在Access 2010 Runtime中使用Office 2000校对工具(Use Office 2000 proofing tools in Access 2010 Runtime)
  • 从单独的Web主机将图像传输到服务器上(Getting images onto server from separate web host)
  • 从旧版本复制文件并保留它们(旧/新版本)(Copy a file from old revision and keep both of them (old / new revision))
  • 西安哪有PLC可控制编程的培训
  • 在Entity Framework中选择基类(Select base class in Entity Framework)
  • 在Android中出现错误“数据集和渲染器应该不为null,并且应该具有相同数量的系列”(Error “Dataset and renderer should be not null and should have the same number of series” in Android)
  • 电脑二级VF有什么用
  • Datamapper Ruby如何添加Hook方法(Datamapper Ruby How to add Hook Method)
  • 金华英语角.
  • 手机软件如何制作
  • 用于Android webview中图像保存的上下文菜单(Context Menu for Image Saving in an Android webview)
  • 注意:未定义的偏移量:PHP(Notice: Undefined offset: PHP)
  • 如何读R中的大数据集[复制](How to read large dataset in R [duplicate])
  • Unity 5 Heighmap与地形宽度/地形长度的分辨率关系?(Unity 5 Heighmap Resolution relationship to terrain width / terrain length?)
  • 如何通知PipedOutputStream线程写入最后一个字节的PipedInputStream线程?(How to notify PipedInputStream thread that PipedOutputStream thread has written last byte?)
  • python的访问器方法有哪些
  • DeviceNetworkInformation:哪个是哪个?(DeviceNetworkInformation: Which is which?)
  • 在Ruby中对组合进行排序(Sorting a combination in Ruby)
  • 网站开发的流程?
  • 使用Zend Framework 2中的JOIN sql检索数据(Retrieve data using JOIN sql in Zend Framework 2)
  • 条带格式类型格式模式编号无法正常工作(Stripes format type format pattern number not working properly)
  • 透明度错误IE11(Transparency bug IE11)
  • linux的基本操作命令。。。