我有一个抽象类,IPacket。
/*
* Represents an abstract network packet.
*/
class IPacket
{
public:
virtual void read(...) = 0;
virtual void write(...) = 0;
virtual int getID() const = 0;
};
我想(并且一直)返回一个指向这样的指针:
class PacketMedium
{
/*!
* Returns a pointer to the next pending packet or NULL if there are no packets pending.
* \note The object must deleted when you are finished with it.
*/
IPacket* receivePacket();
};
现在显然,这是不好的做法;要求调用者删除甚至没有自己分配的指针。我相信,惯例是使用智能指针,即
class PacketMedium
{
public:
std::unique_ptr<IPacket*> receivePacket();
};
但是因为这是库代码,智能指针是nono,尽管事实上我更愿意避免使用它们。
我最好的选择是什么?
感谢您的帮助:)
编辑:之前已经提出过这个问题并给出了非常好的答案,尽管它们都提出了智能指针,或者只是没有在堆上进行分配。鉴于IPacket
是抽象的,堆栈分配将不起作用。
答案 0 :(得分:2)
一个想法是返回一个参考:
class PacketMedium {
public:
IPacket &receivePacket();
private:
IPacketImpl1 impl1;
IPacketImpl2 impl2;
};
receivePacket应按以下方式实施:
IPacket &receivePacket() {
int data = receiveint();
if (data==0) { // impl1
float data = receivefloat();
impl1.data = data;
return impl1;
} else { // impl2
std::string data = receivestr();
impl2.str = data;
return impl2;
}
}
请注意,使用引用时会有一些基本规则:
要解决这些问题,您可以为IPacket接口实现新功能:
virtual IPacket *clone() const=0;
答案 1 :(得分:0)
您甚至不需要返回引用。您可以将公共/相关IPacket
接口包装在一个也具有高级操作的类后面,并在消耗时处理传入数据包的删除(即您的类型调用receivePacket()
)。
如果您主要关心二进制兼容性,那么您可以编写自己的简单unique_ptr。
答案 2 :(得分:0)
另一种解决方案是“处理”。您可以将unique_ptr包装为类似这样的类型,而不是unique_ptr<IPacket> reveivePacket()
:
struct IPacketHandle { int id; };
IPacketHandle receivePacket();
但是那么数据包存储在哪里?它位于std::vector<IPacket*> vec
内,但它在您的代码内部。然后做vec.push_back(new IPacketImpl1); handle.id = vec.size()-1;
。请注意,从向量中删除项可能更复杂/应该用NULL指针替换对象。
但是等等,现在你丢失了IPacket接口的读/写功能。需要再次添加它们:
void read_packet(IPacketHandle h, ...) { vec[h.id]->read(...); }
void write_packet(IPacketHandle h, ...) { vec[h.id]->write(...); }
(我已经在库中成功使用了这个解决方案,并且它提供了类似于haskell标准库的良好的函数编程接口,其中根本没有库用户的内存管理) (还需要仔细考虑std :: vector的位置,以便不需要使用全局变量)