在 C++ 中实现包含有效负载的类的最佳做法
Best practices to implement a Payload-containing class in C++?
我有一个关于层次结构、引用和指针的问题......当我尝试做以下事情时,我想到了这个问题:
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 */
}
现在想象一下,我有很多类型的有效载荷,所有这些有效载荷都是超类(通用)有效载荷的子级。
我想读取标题并切换命令。 例如,如果命令为 1,我创建一个PayloadReset : Payload
并填写其所有属性,然后我想在我的数据包上设置此有效负载(向上投射)。在程序的其他部分,我想读取我当前的数据包,然后读取命令字段并根据命令字段向下转换为适当的类型。
我可以毫无问题地进行向上转换,但是当我尝试对特定有效负载进行向下转换时,问题就出现了,在我们的示例中 PayloadReset。
回答第一个问题(它隐藏在第一个代码示例中的注释中):
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的垃圾收集器。现在,这将成为您的工作。
标记到山姆的答案。
-
在继续之前,请了解堆栈和堆分配之间的区别。在您发布的示例中,您将
Payload p;
对象分配给堆栈 - 这意味着此时对象的大小是已知的,并且所述大小将在堆栈上分配。如果要将派生对象分配给p
,则不起作用,因为所述对象的大小可能不同。这就是为什么你声明一个指向对象的指针(64 位体系结构上为 8 个字节,32 位架构上为 4 个字节),然后在知道要分配哪种类型的派生对象时,使用new
运算符执行此操作,如下所示:Payload *p; p = new PayloadReset(...);
-
上述方法需要手动管理内存,即在分配
new
指针上调用delete
。从 C++11 开始,建议使用<memory>
标头中的智能指针。这些本质上是自动调用delete
的引用计数指针。std::shared_ptr<Payload> p; p = std::make_shared<PayloadReset>(...);
你的问题与Java语法有些关系,但主要是关于面向对象编程。
首先,您应该花点时间熟悉 Java 命名约定。您可以在网络上找到常用的建议。下面是 Java 命名约定的一个示例。我提出这个是因为单个变量名称通常不是一个好主意,并且随着程序规模的扩大,特别是如果团队中有多个人,使用描述性变量名称会带来红利。因此,与其使用Payload p
Payload payload
.
其次,在OO(面向对象)中,最好始终将类实例变量保密,而不是公共。仅在必要时授予对这些变量的访问权限,并通过提供公共方法来屏蔽对这些变量的访问。所以,在你的类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;
}
}
另外,为了回答有关有效负载命名的更多问题。就像我之前说的..使用描述性名称。Java 没有像 C 那样的指针引用,通常为您处理内存管理,因此不需要或不支持&
。
您的最后一个问题/主题实际上又是关于OO和类层次结构的。似乎有效负载将是一个通用基类,您可能有多个特定的"有效负载类型",例如ResetPayload
。如果是这种情况,则需要定义Payload
并创建扩展Payload
的 ResetPayload
类。我不确定您要做什么,但是将类/对象广告名词和方法视为动词。还要考虑"is-a"和"has-a"的概念。据我所知,也许Payload
所有的"has-a command and an address. Also, maybe each
Payload also has multiple
Packet s, whatever. Just as an example, you would then define your
Payload"类都是这样的:
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
类型(如 Reset),您将创建类、扩展Payload
并提供特定于此类型的其他属性/操作,如下所示:
public class ResetPayload extends Payload{
public ResetPayload(int addy, int comm){
super(addy, comm);
}
public void reset(){
//Do stuff here to reset the payload
}
}
希望这能回答您的问题并推动您走得更远。祝你好运。
这是我对一般问题的看法,它扩展了标记的联合思想。优点是 1.)没有继承/dynamic_cast 2.)没有共享的 PTR 3.)豆荚 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-> ...
}
只需检查标记是否包含预期的数据(类型),然后将数据转换为指针类型。
免责声明:它不是完整的类。此处缺少某些访问成员函数。
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- 在c代码之间共享数据的最佳方式
- 使用std::source_location报告错误的最佳实践
- 派生类销毁的最佳实践是什么
- 将寄存器设计成可由C和C++访问的外设的最佳实践
- 在两台机器之间进行时间戳的最佳c++chrono函数是什么
- 使用QQuickFramebufferObject时同步数据的最佳方式是什么
- 在C++中向零方向近似的最佳方法
- 使用不同的CRT将新的C++代码与旧的(二进制)组件隔离开来的最佳方法是什么
- 从嵌套在std::映射中的std::列表中删除元素的最佳方式
- 如果条件为TRUE(最佳方式?),则在do while循环中后置增量
- 检测win32服务创建和删除的最佳方法
- 在reactor中存储eventHandlers的最佳方式是什么
- 在C++中样板"冷/never_inline"错误处理技术的最佳方法是什么?
- 在 c++ 中对类中的 c 字符串动态数组进行排序的最佳方法是什么?
- 在AVX通道中混洗的最佳方式
- 使用 CPR 发送 nlohmann json 有效负载的最佳方法C++请求库
- 在 C++ 中实现包含有效负载的类的最佳做法