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 ...):
解决这个问题的最佳方法是什么? 如果我使用合成,我将无法访问我想要重用的函数。 但是已知使用私有继承是hacky,特别是因为我必须手动编写包装函数来公开我希望客户端使用的基类部分(即“getter”函数)......
有没有办法阻止dynamic_casts或拦截它们? 在这种情况下,从派生类到基类的转换是有意义的。 但是,只有当标头与我的特定数据包的签名匹配时,才应该从基类转换到派生类。
包含一个构造函数是否有意义,该构造函数将基类作为派生类的参数? 这有特别的名字吗? 以下是我的想法:DerivedClass&DerivedClass(const BaseClass&base); 基本上我会检查标头签名,然后只有在标头签名与特定数据包的情况匹配的情况下才完成构造。
现在我通过询问从基础到派生的转换和构造函数来打开一堆蠕虫......等于/不等式/赋值运算符等等? 我是否必须编写每个特定案例来检查派生与基础? 例如,如果在执行以下操作时所有基类元素都相同,则可以返回“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
我甚至需要担心派生类的复制构造函数/赋值运算符,如果所有它都是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...):
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)...
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.
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.
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
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
最满意答案
这不行。 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.
相关问答
更多-
根据您发布的内容,似乎_issueRepository未设置为对象 - 它不会在任何地方实例化。 它需要在您可以调用方法之前进行实例化。 From what you posted, it appears that _issueRepository is not set to an object - it is not instantiated anywhere. It needs to be instantiated before you can call methods on it.
-
C#对象类型 - 未将对象引用设置为对象的实例(C# object type - object reference not set to an instance of an object)[2021-08-09]
您的typeUDDBTble不包含索引器,因此您尝试使用方括号访问其对象无效。 我想你的意思是: UDDB.Rec[0].Items = ... 此外,除非您需要其他异常,否则必须初始化Rec的第一个单元格。 UDDB.Rec[0] = new typeRec(); UDDB.Rec[0].Items = new object[1] Your typeUDDBTble does not contain an indexer, so you trying to access its object with ... -
如何(如果可能)获取内存中对象(类实例)的引用?(How to (if possible) get the reference of an in-memory object (class instance)?)[2024-02-16]
我希望我能正确理解你的问题,因为我不确定你的意思是“为了调试的目的”,但是这里是: 您可以使用以下命令访问在同一会话的内存中加载的另一个程序的变量(我非常肯定它不需要在调用堆栈中): ASSIGN ('(PROGRAM)VARIABLE') TO LV_LOCAL. 有了引用变量,它变得更加棘手,但这里有一个示例将有助于演示。 这里是我们的调用程序,它包含一个我们想要访问其他地方的引用变量LR_TEST 。 为了演示的目的,我参考了一个本地定义的类(因为这是我从你的问题中收集的)。 REPORT ZCA ... -
您需要创建一个Address的新实例,并将其分配给和to : public ActionResult FareCalculator(string from , string to) { var myroute = new Route(); myroute.from = new Address(); // new instance myroute.from.addressDescription = from; myroute.from.addressLatitude = 51 ...
-
C#(对象数组)对象引用未设置为对象的实例(C# (Array of object) object reference not set to an instance of an object)[2023-08-13]
你需要初始化emp[count] 。 您可以通过添加以下内容来执行此操作: foreach (string line in lines) { emp[count] = new employees(); string[] parts = new string[4]; //.... } 当你打电话给employees[] emp = new employees[emp_count]; 你将emp初始化为一emp_count度为emp_count的employees 。 emp[0 ... -
如果你想要一个自己的共享指针,你可以使用Boost的enable_shared_from_this 。 我不知道是否有一种简单的标准方法来完成相同的事情,但它只是一个模板,所以你可以想象没有Boost做同样的事情。 If you want a shared pointer to yourself, you can use Boost's enable_shared_from_this. I don't know if there's an easy standard way to accomplish th ...
-
你的方法是一个实例方法,这意味着你需要一个运行它的实例: using (MDMDataContext dc = new MDMDataContext()) { GetUser user = new GetUser(); user.GetCurrentUser(dc, "ABCD"); } static关键字意味着该方法是静态的 ,并且可用于整个类型,而不是绑定到特定实例。 这就是为什么当它被标记为静态时,你可以使用类名来调用它(并且必须这样调用它)。 Your method ...
-
当你设置: array[0] = foo; 您实际上正在对foo指向的对象的引用进行按值复制,并将该引用复制到“array [0]”中。 以后,当你这样做时: foo = bar; 你正在做同样的事情 - 按值将对bar指向的对象的引用复制到变量foo中。 这对array [0] 没有影响 ,因为引用最初是按值复制的。 为了使第一个DerivedClass1实例(foo的原始引用)成为GC的候选者,您需要将array [0]显式设置为其他引用(或null)。 When you set: array[0] ...
-
C#类实例持有字符串引用但不是对象引用(C# Class Instance Holding String Reference But Not Object Reference)[2023-07-06]
这不行。 RedirectToAction导致302浏览器无法携带复杂的模型。 有一些替代方法,您可以使用TempData来存储模型,执行重定向和检索数据。 您甚至可以直接调用EatCandy操作而不是重定向,但是自动匹配视图将无法工作,您必须在EatCandy操作结束时强制使用“EatCandy”视图名称。 This won't work. RedirectToAction results in a 302 to the browser which just is not capable of carry ... -
用以下内容替换“搜索”部分: if (!string.IsNullOrEmpty(searchValue)) { UserList = UserList.Where(a => (a.firstname != null ? a.firstname.Contains(searchValue) :false) || (a.lastname != null ? a.lastname.Contains(searc ...