class 中的指针私有属性,其中此 class 在向量中使用,生成错误双重释放或损坏

Pointer private atribute in a class, where this class is using in a vector, generate error double free or orruption

我已经在 C++ std c++11 中实现了 class,代码二测试 winstrument class 运行良好,但是 station.cpp 的代码不起作用,它在

中失败
stationA.insertInstrument(instrumentC);

但在此之前我又插入了两个工具,内部容器是一个向量,我使用方法push_back来存储工具:

void WStation::insertInstrument(const WInstrument& instrument)
{
   _instruments.push_back(instrument);
}

这是class

的原型
class WInstrument
{
  public:
    WInstrument(int serialNumber, std::string description, Type type);
    ~WInstrument();

    bool operator==(const WInstrument& rhs) const;
    bool operator!=(const WInstrument& rhs) const;

    double read() const;

    std::string getDescription() const;
    int getSerialNumber() const;
    Type getType() const;

  private:
    int _serialNumber = -1; 
    Type _type;
    std::string _description;

  private:
    std::uniform_real_distribution<double> *_dist;
};

这是默认构造函数:

WInstrument::WInstrument(int serialNumber, std::string description, Type type):
  _serialNumber(serialNumber),
  _type(type),
  _description(description)
{
  switch(_type)
  {
    case wind:
      {
    _dist = new std::uniform_real_distribution<double>(0.0, 200.0);
    break;
      }
    case temperature:
      {
    _dist = new std::uniform_real_distribution<double>(-20.0, 100.0);
    break;
      }
    case humidity:
      {
    _dist = new std::uniform_real_distribution<double>(0.0, 100.0);
    break; 
      }
  }
}

我认为问题出在 *_dist,因为如果我不使用指针并直接实现,那么我将在其中使用函数 read,代码可以工作。 在析构函数中,我使用 delete _dist。问题是我没看懂问题

完整代码在https://github.com/retiarus/test-winstrument.

中可用

问题是 WInstrument 包含一个拥有 raw-pointer 到 uniform_real_distributiondeleted 在你的析构函数中,但你的 class 没有正确的复制构造函数或复制赋值运算符(参见 The Rule of Three)。

因为你没有定义复制构造函数或复制赋值运算符,编译器已经自动为你创建了一个,但它只是愚蠢地复制了拥有者 raw-pointer。

这里:

void WStation::insertInstrument(const WInstrument& instrument)
{
  _instruments.push_back(instrument);
}

您正在将 WInstrument 的副本复制到 std::vector 中,现在您有两个 WInstrument 的副本,它们都带有指向同一个 uniform_real_distribution 的指针。当调用这两个对象的析构函数时,您会得到双重删除,因此 error.

最简单的解决方案就是根本不使用指针,直接存储 uniform_real_distribution:

class WInstrument
{
  public:
    WInstrument(int serialNumber, std::string description, Type type);

  //...

  private:
    std::uniform_real_distribution<double> _dist;
};

WInstrument::WInstrument(int serialNumber, std::string description, Type type):
  _serialNumber(serialNumber),
  _type(type),
  _description(description)
{
  switch(_type)
  {
    case wind:
      {
    _dist = std::uniform_real_distribution<double>(0.0, 200.0);
    break;
      }
      // ...
  }
}

那么你根本不需要定义析构函数,问题就迎刃而解了。

Live demo.

次要细节:如果按值存储 uniform_real_distribution,它会强制您处理 const 正确性,因为 uniform_real_distribution 上的 operator() 不是 const。一种解决方案是使 WInstrument::read() non-const。另一种解决方案是使 dist_ 可变,以便您可以在 const 成员函数中使用它。虽然你必须小心,因为通常 const 成员函数是 thread-safe 而这个不是

当您 push_back a const WInstrument & 时,您正在将该 WInstrument 复制到向量中。由于您没有实现复制构造函数,因此它使用默认的复制构造函数。这将复制您的序列号、类型和描述,最重要的是,您的分发指针。这些指针就是这样复制的:指针。

当您在 insertInstrument 上调用的 WInstrument 超出范围时,将调用其析构函数,并释放分配。但是向量中 WInstrument 中的指针仍然指向这个已释放的内存。

最简单的解决方法是为 _dist 切换为使用智能指针。其他解决方案是创建一个复制构造函数来正确处理 _dist 的复制,或者在 insertInstrument.

中使用移动语义