Search code examples
c++hierarchydowncast

Best practices to implement a Payload-containing class in C++?


I have a question about hierarchy, references and pointers... The question comes to my mind when I had tried to do the following stuff:

class packet {
public:
    int address;
    int command; /**< Command select the type of Payload that I must decode */
    Payload p; /**< Generic payload, first question:
                    Payload p or Payload * p or Payload &p ?
                    I have a background in C, for this reason I prefer
                    Payload p but I know that this is not recommended for C++ */

private:
    /** All getter and setter for attributes */
    /** Second question: What is the best way to implement a getter
        and setter for Payload?... I prefer something
        similar to Java if this is possible */    
}

Now imagine that I have a lot of types of Payload, all these payloads are children of the super class (generic) Payload.

I want to read the header and switch o the command. For example, if command is 1 I create a PayloadReset : Payload and fill in all of its attributes, then I want to set on my packet this payload (up-casting). In other part of the program I want to read my current packet and then read the command field and down-cast to the appropriate type depending on the command field.

When I tried to do this, I could do the up-casting without problems but the problem comes when I tried to do the downcasting to the specific Payload, in our example PayloadReset.


Solution

  • Tagging onto Sam's answer.

    1. Before you go any further, learn the difference between stack and heap allocation. In the example you posted, you're allocating your Payload p; object on the stack - implying that the size of the object is known at this point and said size will be allocated on the stack. If you wanted to assign an derived object to p, it wouldn't work, because said object will likely be of different size. This is why you instead declare a pointer to the object (8 bytes on 64-bit architecture, 4 bytes on 32 bit), and then when you know which type of derived object you want to allocate, you do it using the new operator, as such:

      Payload *p;
      p = new PayloadReset(...);
      
    2. The above method would require manually managing memory, i.e. calling delete on the new allocated pointer. As of C++11, the recommendation is to use smart pointers from the <memory> header. These are essentially reference counted pointers that automatically call delete for you.

      std::shared_ptr<Payload> p;
      p = std::make_shared<PayloadReset>(...);