在没有智能指针的情况下返回指向堆对象的指针

时间:2013-07-06 09:25:55

标签: c++ pointers memory

我有一个抽象类,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是抽象的,堆栈分配将不起作用。

3 个答案:

答案 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;
  }
}

请注意,使用引用时会有一些基本规则:

  1. 两次调用receivePacket()是危险的。第二次调用可能会删除现有数据。
  2. 应立即使用您收到的数据。将IPackets存储更长时间是危险的。
  3. 要解决这些问题,您可以为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的位置,以便不需要使用全局变量)