cpp - class that has pure virtual, normal function, static variables and friend class

86 views Asked by At

Does it make sense to create a class which holds:

  • pure virtual functions
  • and also virtual functions
  • and also regular methods
  • and static functions
  • and friend class declaration

I'm asking from a "design practice" point of view.

Without tiring you with too many details, I have something that looks a bit like the following class:

class CommonMessage {
protected:
   struct CommonHeader hdr;
   static const int hdrSize = sizeof(CommonHeader);
   friend class Parser; // parser show call serializeHdr() on an incoming buffer
public:
   bool desrializeHdr(uint8_t *buf, uint32_t offset);    // regular method
   virtual serializePayload(uint8_t *buf, uint32_t offset) = 0;
   virtual deSerializePayload(uint8_t *buf, uint32_t offset) = 0;
   
}

My questions are basically:

  1. Is it true that once you have a pure virtual, you shouldn't (i.e good practice) include regular methods, and/or static functions/variables or to declare an outside friend class?

As for now, the above example serves exactly what I need: I can create derived classes which have hdr, and a different class Parser (declared as "friend") which can parse any stream according to a specific State-Machine (must be familiar with the CommonMessage class, for example headerSize etc...) so this suggested implementation does the job.

  1. Is it a good practice, and if not, what would you suggest to change? Or what guidelines should I follow? or are there any rules of thumb in cases like that that I should follow? (for example, do not mix A and B at the same class, for instance).
1

There are 1 answers

0
bitmask On

The reason this is often considered a code smell is because it looks like code-reuse by use of polymorphy. But polymorphy isn't for code-reuse. Templates are.

So, first of all, if you have a class with pure virtual member functions and no virtual destructor. That class likely shouldn't have any virtual members.

In general, if you can, try to use templates. In your case, what I would probably do is have a thing that knows how to serialise a given payload (maybe even the payload itself knows this). Then plug together a message type template that knows how to plug payload serialisation together with header serialisation.

If you can only decide at runtime which payload you will have to handle, pull the dispatch out as much as possible and instantiate the correct type early. This will likely mean you will have to pass along template arguments for most in-between steps.

Having said that, there is a point where you can worry too much about adhering to some rigid design mandates. Most guidelines have exceptions.