我有关于层次结构,引用和指针的问题......当我尝试执行以下操作时,我想到了这个问题:
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中。
答案 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的答案。
在进一步学习之前,请先了解堆栈和堆分配之间的区别。在您发布的示例中,您将在堆栈上分配您的Payload p;
对象 - 这意味着此时已知对象的大小,并且该大小将在堆栈上分配。如果要将派生对象分配给p
,它将无法工作,因为所述对象可能具有不同的大小。这就是为什么你改为声明指向对象的指针(64位架构上的8个字节,32位上的4个字节),然后当你知道要分配哪种类型的派生对象时,你可以使用{{1运算符,如:
new
上述方法需要手动管理内存,即在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-> ...
}
只需检查标记是否包含预期数据(类型)并将数据转换为指针类型。
免责声明:这不是完整的课程。这里缺少一些访问成员功能。