我有一个基于网络(TCP-IP)的自定义IPC系统。

考虑代码(以及下面的解释):

#include "boost/shared_ptr.hpp"
#include <string>

using namespace std;

class TCommand {
public:
  typedef boost::shared_ptr<TCommand> Ptr;

  TCommand() {
      cout << "    Creating TCommand..." << endl;
  }

  virtual ~TCommand() {
      cout << "    Destroying TCommand..." << endl;
  }

  static TCommand * factory(int classID);

  virtual void parse(const char *data, int dataSize) = 0;
  virtual void print() = 0;
  virtual std::string getType() = 0;

};


class TPingCommand : public TCommand {
public:
  static const int classID = 1;
  int value;

  TPingCommand() : TCommand() {
      cout << "    Creating TPingCommand..." << endl;
  }

  virtual ~TPingCommand() {
      cout << "    Destroying TPingCommand..." << endl;
  }

  virtual void parse(const char *data, int dataSize) {
    if (dataSize < 4) throw 1;

    this->value = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
  }

  virtual void print() {
      cout << "  TPingCommand:" << endl;
      cout << "    value = " << dec << this->value << " (0x" << hex << this->value << ")" << endl;
  }

  virtual std::string getType() {
      return "TPingCommand";
  }
};

class TOtherCommand : public TCommand {
public:
  static const int classID = 2;
  int value;
  char value2;
  short int value3;

  TOtherCommand() : TCommand() {
      cout << "    Creating TOtherCommand..." << endl;
  }

  virtual ~TOtherCommand() {
      cout << "    Destroying TOtherCommand..." << endl;
  }

  virtual void parse(const char *data, int dataSize) {
    if (dataSize < 7) throw 1;

    this->value = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
    this->value2 = data[4];
    this->value3 = data[5] << 8 | data[6];
  }

  virtual void print() {
      cout << "  TOtherCommand:" << endl;
      cout << "    value  = " << dec << this->value << " (0x" << hex << this->value << ")" << endl;
      cout << "    value2 = " << dec << this->value2 << " (0x" << hex << (int)this->value2 << ")" << endl;
      cout << "    value3 = " << dec << this->value3 << " (0x" << hex << this->value3 << ")" << endl;
  }

  virtual std::string getType() {
      return "TOtherCommand";
  }
};


TCommand * TCommand::factory(int classID) {
    cout << "  Factory for classID = " << dec << classID << " (0x" << hex << classID << ")" << endl;
    switch (classID) {
    case TPingCommand::classID: return new TPingCommand(); break;
    case TOtherCommand::classID: return new TOtherCommand(); break;
    default: throw 1;
    }
  }


TCommand::Ptr receiveFromNetwork(int test, TCommand::Ptr knownCommand)
{
    // Receive command header from network.
    // int classID is the command class internal ID.
    // int dataSize is the command's body size in bytes.
    // For instance:
    //   int classId = 2;
    //   int datasize = 7;

    int classId = 1;
    int dataSize = 4;
    char data[10];

    if (test == 0) {
        cout << "  Using test data 0..." << endl;
        classId = 1;
        dataSize = 4;
        data[0] = 0x01; data[1] = 0x02; data[2] = 0x03; data[3] = 0x04;
    } else if (test == 1) {
        cout << "  Using test data 1..." << endl;
        classId = 2;
        dataSize = 7;
        data[0] = 0x11; data[1] = 0x12; data[2] = 0x13; data[3] = 0x14; data[4] = 0x41; data[5] = 0x16; data[6] = 0x17;
    }

    TCommand::Ptr cmd;
    if (knownCommand == 0) {
        cout << "  No command provided." << endl;
        cmd.reset(TCommand::factory(classId));
        cout << "  Command created from factory: " << cmd->getType() << endl;
    } else {
        cmd = knownCommand;
        cout << "  Command provided: " << cmd->getType() << endl;
    }

    cout << "  Parsing data..." << endl;
    cmd->parse(data, dataSize);

    // The command was identified as TOtherCommand (classID = 2).
    // The factory returned a TOtherCommand instance.
    // The TOtherCommand's parse method will check the dataSize is suitable (7 bytes are necessary).
    // The parse method will unserialize data to their fields.
    // This way, the fields would be:
    //    data = 0x11121314;
    //    data2 = 0x42; // 'A' as char.
    //    data3 = 0x1213;

  return cmd;
}

void caller() {
    // Case 1 (ok):
    // I know I'm going to receive a TPingCommand.
    cout << "Test case 1:" << endl;
    TCommand::Ptr known(new TPingCommand());
    TCommand::Ptr cmd1 = receiveFromNetwork(0, known);
    cmd1->print();

    // Case 2 (problems):
    cout << "Test case 2:" << endl;
    TCommand::Ptr dummy;
    TCommand::Ptr cmd2 = receiveFromNetwork(1, dummy);
    cmd2->print();

    cout << "Teardown..." << endl;
}

int main() {
    caller();
}

receiveFromNetwork是一种经过修改的工厂方法,用于从网络接收命令,但是,在某些情况下,通过先验,我知道我将要接收哪种类型的命令,因此我创建了它的实例并传递给函数(如knownCommand)。 命令类是从TCommand类派生的。 该函数返回knownCommand(该命令不是必需的,因为您已将其作为参数传递,但在其他情况下很有用)。

在所有其他情况下,从网络接收到的前几个字节描述了命令classID,我使用它在此函数内创建合适的TCommand实例。 然后从网络数据中解析命令,并在函数末尾返回该命令。 knownCommand只是一个虚拟的TCommand实例。

当我传递knownCommand时,它运行良好。 当我将虚拟命令作为参数传递时,它崩溃了(据我所知是双重释放)。

我考虑过将TCommand引用用于knownCommand,但是我无法做到这一点,因为我必须返回一个共享指针,这将导致相同的原始指针由两个不同的共享指针实例(一个来自调用者)进行管理。方法以及另一个在receiveFromNetwork方法内部)。

有人对如何解决这个问题有个好主意吗?

这是有问题的场景的部分valgrind输出:

==31859== Thread 2:
==31859== Invalid read of size 4
==31859==    at 0x805D7B0: boost::detail::sp_counted_impl_p<TVideoGetSourceSizeCommand>::dispose() (checked_delete.hpp:34)
==31859==    by 0x407FF42: Server::receiveFromNetwork() (sp_counted_base_gcc_x86.hpp:145)
==31859==    by 0x40800AF: Server::serverThread(void*) (Server.cpp:107)
==31859==    by 0x434496D: start_thread (pthread_create.c:300)
==31859==    by 0x42B398D: clone (clone.S:130)

非常感谢。

#1楼 票数:0 已采纳

找到了问题。

在代码的调用部分,有一个shared_ptr被错误地“复制”了。 产生的错误是这样的:

boost::shared_ptr<DerivedType> ptr1(new DerivedType(...));
boost::shared_ptr<BaseType> ptr2(ptr1.get());

当然,这将导致重复的“免费”和程序崩溃。

代码不是那么愚蠢,但最终是简化的错误场景。 即使这是一个小错误。

感谢所有帮助人员。

  ask by Marcus translate from so

未解决问题?本站智能推荐:

2回复

工厂方法创建shared_ptr对象

当使用工厂创建对象时,例如下面的示例,在某些情况下,由shared_ptr包装的对象显然会在返回过程中被删除(在调试过程中,该对象已创建,但是将其分配给this->xs引发异常)。 当我更改工厂方法以返回原始指针,并且Link::xs成员成为unique_ptr ,代码运行良好。 sh
10回复

在库的公共接口中使用boost::shared_ptr

我们有一个C ++库,我们提供给几个不同的客户端。 最近我们改用了在公共接口中使用原始指针而不是使用boost :: sharedptr。 正如您可能猜到的那样,这提供了巨大的好处,因为现在客户不再需要担心谁需要删除什么以及何时删除。 当我们进行切换时,我认为这是正确的做法,但是让我感到困
1回复

使用boost::shared_ptr的多态调度

我试图基于向量中指针的派生类型调用重载函数。 我有一个基类Fruit,并创建了shared_ptr的向量。 然后,我将后代的共享指针推入向量。 到目前为止,一切都很好(在我添加代码以打印元素之后出现了问题)。 接下来,我要遍历容器,为每个水果调用适当的Print函数。 我收到编译错
2回复

如何在不是shared_ptr的成员中使用shared_ptr?

我正在研究几个类,我想知道如何在我的应用程序类中使用普通成员,该成员需要使用shared_from_this()? 这是一些代码来阐明我的意思(请参阅评论)
2回复

boost::~shared_ptr如何工作?

在阅读“超越C ++标准库:Boost简介”时,我得到了一个非常有趣的例子: 我做了一些测试: 我在这里很好奇的是~hared_ptr是如何工作的? 它如何推导派生类B? 谢谢你的帮助! 谢谢大家,我写了一个关于~share_ptr如何工作的简单示例 关键是: 模板构
3回复

当boost::shared_ptr可能没有被释放?

阅读此主题C ++面试准备 (马特的答案)后,我有一个关于boost :: shared_ptr的问题。 shared_ptr是否真的有可能泄漏内存? 怎么样?
4回复

boost::shared_ptr和继承

我面临的情况是我有一个基类的boost::shared_ptr的std::vector 。 在我的程序过程中,我需要将共享指针存储到该向量中的派生类对象,并且稍后在程序中,需要检索这些共享指针。 以下代码说明了我的问题: 在上面的代码中,如果我更改BOOST_FOREACH的代码
1回复

使用Boost的shared_ptr和weak_ptr构造树

这是正确的方法,对左右节点使用boost::shared_ptr ,对根节点使用boost::weak_ptr 。 我知道分享是一个过分的杀伤力。 我稍后将需要它。 节点类,2个儿子共享ptr 类树,弱ptr