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。问题是我没看懂问题
中可用
问题是 WInstrument
包含一个拥有 raw-pointer 到 uniform_real_distribution
的 deleted
在你的析构函数中,但你的 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;
}
// ...
}
}
那么你根本不需要定义析构函数,问题就迎刃而解了。
次要细节:如果按值存储 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
.
中使用移动语义
我已经在 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。问题是我没看懂问题
中可用问题是 WInstrument
包含一个拥有 raw-pointer 到 uniform_real_distribution
的 deleted
在你的析构函数中,但你的 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;
}
// ...
}
}
那么你根本不需要定义析构函数,问题就迎刃而解了。
次要细节:如果按值存储 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
.