在C ++中实现包含Payload的类的最佳实践?

时间:2016-03-04 11:49:12

标签: c++ hierarchy downcast

我有关于层次结构,引用和指针的问题......当我尝试执行以下操作时,我想到了这个问题:

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 */    
}

现在假设我有很多类型的Payload,所有这些有效负载都是超类(通用)Payload的子级。

我想读取标题并切换命令。例如,如果命令为1,我创建一个PayloadReset : Payload并填写其所有属性,然后我想在我的数据包设置这个有效负载(向上转换)。在程序的其他部分,我想读取当前数据包,然后读取命令字段并根据命令字段向下转换为适当的类型。

当我尝试这样做时,我可以毫无问题地进行向上投射,但是当我尝试向特定的Payload进行向下转换时,问题出现了,在我们的示例PayloadReset中。

4 个答案:

答案 0 :(得分:2)

回答第一个问题(在第一个代码示例中隐藏在评论中:

Payload *p;

作为从Java到C ++过渡的一部分,您需要学习的第一件事是指针是什么以及它们如何工作。一段时间以来,让您感到困惑的是,Java中的所有对象都是指针。在使用Java时,您永远不需要知道这一点。但是你必须知道现在,为了理解C ++。因此,将C ++类声明为

Payload p;

与在Java中进行类似声明不同。在Java中没有与此声明等效的内容。在Java中,您确实有一个指针,您必须使用new关键字对其进行实例化。那部分Java最初是从C ++开始的。这与C ++的过程相同,只是您必须将其显式声明为指针。

Payload *p;

然后,在其他地方,使用PayloadReset子类的示例:

class PayloadReset : public Payload { /* Class declaration */ };

PayloadReset *r = new PayloadReset( /* Constructor argument */ };

p=r;

作为从Java到C ++的事务的一部分,您需要学习的第二件事是delete所有实例化对象的时间和方式。你在这里没有Java的垃圾收集器。现在,这成了你的工作。

答案 1 :(得分:0)

您的问题与Java语法有些相关,但主要与面向对象编程有关。

首先,您应该花点时间熟悉Java命名约定。您可以在网络上找到常用的建议。以下是Java Naming Conventions的一个示例。我提出这个问题是因为单个变量名称通常不是一个好主意,并且随着程序规模的增长,具有描述性变量名称会带来好处,特别是如果团队中有多个人。因此,而不是Payload p使用Payload payload

其次,在OO(面向对象)中,最好始终将Class实例变量保持为私有,而不是公共变量。仅在必要时才允许访问这些变量,并通过提供公共方法来屏蔽对它们的访问。因此,在您的班级Packet的示例中,您的公共/私人是倒退的。你的课看起来应该更像:

public class Packet{
    //private fields
    private int address;
    private int command;
    private Payload payload;

    //Maybe provide a nice constructor to take in expected
    //properties on instantiation
    public Packet(Payload pay){

    }

    //public methods - as needed
    public void getPayload(){
       return this.payload;
    }

    public void setAddress(int addy){
        this.address = addy;
    }

    public int getCommand(){
       return this.command;
    }
}

另外,回答有关Payload命名的更多问题。就像我之前说的那样......使用描述性名称。 Java没有像C这样的指针引用,通常会为您处理内存管理,因此不需要或不支持&

你的最后一个问题/主题是关于OO和Class heirarchy的。 Payload似乎是一个通用的基类,你可能有多个特定的“有效载荷类型”,如ResetPayload。如果是这种情况,您可以定义Payload并创建扩展ResetPayload的{​​{1}}类。我不确定你想要做什么,但将类/对象广告名词和方法视为动词。还要考虑'is-a'和'has-a'概念。从我看到的情况来看,也许所有Payload s'都有Payload有效负载command and an address. Also, maybe each数据包also has multiple Payload`类,如下所示:

s, whatever. Just as an example, you would then define your

然后,如果您的类型public class Payload{ private int address; private int command; private List<Packet> packets = new ArrayList<>(); public Payload(int addy, int comm){ this.address = addy; this.command = comm; } public void addPacket(Packet p){ packets.add(p); } public List<Packet> getPackets(){ return this.packets; } public int getCommand(){ return this.command; } public int getAddress(){ return this.address; } } 更具体,比如重置,您将创建类,扩展Payload并提供特定于此类型的其他属性/操作,这类似于:

Payload

希望,这可以回答您的问题并让您更进一步。祝你好运。

答案 2 :(得分:0)

标记Sam的答案。

  1. 在进一步学习之前,请先了解堆栈和堆分配之间的区别。在您发布的示例中,您将在堆栈上分配您的Payload p;对象 - 这意味着此时已知对象的大小,并且该大小将在堆栈上分配。如果要将派生对象分配给p,它将无法工作,因为所述对象可能具有不同的大小。这就是为什么你改为声明指向对象的指针(64位架构上的8个字节,32位上的4个字节),然后当你知道要分配哪种类型的派生对象时,你可以使用{{1运算符,如:

    new
  2. 上述方法需要手动管理内存,即在Payload *p; p = new PayloadReset(...); 分配的指针上调用delete。从C ++ 11开始,建议使用new标头中的智能指针。这些基本上是引用计数指针,会自动为您调用<memory>

    delete

答案 3 :(得分:0)

这是我对一般问题的看法,它扩展了标记的联合理念。优点是1.)没有继承/ dynamic_cast 2.)没有共享ptr 3.)POD 4.)rtti用于生成唯一标签:

using cleanup_fun_t = void(*)(msg*);

class msg
{
public:
    template<typename T, typename... Args>
    static msg make(Args&&... args);
private:
    std::type_index tag_;
    mutable std::atomic<cleanup_fun_t> del_fn_; // hell is waiting for me, 
    uint64_t meta_;
    uint64_t data_;
};

请填写所有好的会员功能。这个班只是移动。您正在使用静态成员函数make创建具有有效负载的消息:

template<typename T, typename... Args>
msg msg::make(Args&&... args)
{
     msg m;
     m.tag_ = typeid(T);
     m.del_fn_ = nullptr;
     if (!(std::is_empty<T>::value))
     {
         auto ptr = std::make_unique<T>(std::forward<Args>(args)...);
         m.data_ = (uint64_t)ptr.release();
         m.del_fn_ = &details::cleanup_t<T>::fun; // deleter template not shown
     }
     return m;
 }

 // creation:
 msg m = msg::make<Payload>(params passed to payload constructor);
 // using
 if (m.tag() == typeid(Payload))
 {
     Payload* ptr = (Payload*)m.data;
     ptr-> ...
 }

只需检查标记是否包含预期数据(类型)并将数据转换为指针类型。

免责声明:这不是完整的课程。这里缺少一些访问成员功能。