未分配正在释放的 C++ 指针(可能是 unique_ptr 或 boost::ublas 的问题)
C++ Pointer being freed was not allocated (possibly, an issue with unique_ptr or boost::ublas)
这是我之前 questions 之一的后续。我正在处理的问题在上述问题的表述中有详细解释。不幸的是,我无法提供展示问题的最小示例。
在这个问题中,我试图重新定义问题并提供一个最小的例子。下面示例中的代码执行并完成了它应该做的事情。但是,在前面 question 中介绍的稍微复杂的情况下,有时会导致运行时错误
dynamic_links(3941,0x7fff749a2310) malloc: *** error for object 0x61636f6c65720054: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
不幸的是,我只能在优化设置为 -O3
(也可能是 -O2
)时产生错误。这在使用调试工具时会产生问题。不幸的是,我无法通过 no/minimal 代码优化重现该问题。作为参考,我使用 gcc 4.9.1
.
在当前问题中,我想了解我正在使用的 class 继承机制的结构设计从动态内存分配的角度来看是否可能存在危险。请找到以下代码:
namespace ublas = boost::numeric::ublas;
template<typename TScalarType = double>
using ublasRn = ublas::vector<TScalarType>;
class Base
{
public:
virtual ~Base(void) = 0;
};
Base::~Base(void){}
template<typename T1, typename T2>
class Composite : public Base
{
protected:
T1 T1Instance;
std::unique_ptr<T2> u_T2Instance;
public:
Composite(){}
virtual ~Composite(void){}
const std::type_info& returnT1TypeID(void) const
{return typeid(T1);}
const std::type_info& returnT2TypeID(void) const
{return typeid(T2);}
};
template<typename T2>
class CompositeCT: virtual public Composite<double, T2>
{
public:
using Composite<double, T2>::Composite;
virtual ~CompositeCT(void)
{}
};
template<typename T1>
class CompositeRn: virtual public Composite<T1, ublasRn<double>>
{
public:
using Composite<T1, ublasRn<double>>::Composite;
virtual ~CompositeRn(void){}
};
class CompositeCTRn :
public CompositeCT<ublasRn<double>>,
public CompositeRn<double>
{
public:
CompositeCTRn(void):
Composite<double, ublasRn<double>>(),
CompositeCT<ublasRn<double>>(),
CompositeRn<double>()
{};
};
template<typename T1, typename T2, class TComposite>
class Trajectory: public Base
{
protected:
std::vector<std::unique_ptr<TComposite>> m_Trajectory;
public:
Trajectory(void)
{checkType();}
void checkType(void) const
{
TComposite CI;
if (
!(
CI.returnT1TypeID() == typeid(T1) &&
CI.returnT2TypeID() == typeid(T2)
)
)
throw std::runtime_error("Error");
}
virtual ~Trajectory(void){}
};
int main()
{
Trajectory<
double,
ublasRn<>,
CompositeCTRn
> T;
std::cout << 123 << std::endl;
}
注意。我正在使用外部库boost::ublas。我相信问题与 ublas
对象的动态内存分配机制有关的可能性不大。
嗯。我在想你真的在推动类型系统。我无法理解为什么你会想要这种令人震惊的类型层次结构:
在大约 60 行代码中造成了很大的破坏。我想不出 Liskov Substitution Principle 适用于此层次结构的合理情况。
I also note that this kind of emphasis on runtime polymorphism does seem to run counter to the design goals of C++ and uBlas in particular.
尤其是这一行(本质上声明了图表的中心):
class CompositeCTRn : public CompositeCT<ublasRn<double> >, public CompositeRn<double> {
这有效地声明了具有单个虚拟基的 class,即通过三个基的 "aliased":
using VBase = Composite<double, ublasRn<double> >;
using CTBase = CompositeCT<ublasRn<double> >;
using RnBase = CompositeRn<double>;
这意味着 应该 类型中只有一个 _i1
和一个 _i2
成员。这意味着在所有符合标准的编译器上,以下内容应该编译 运行 而不会触发任何断言或导致内存泄漏等:
class CompositeCTRn : public CompositeCT<ublasRn<double> >, public CompositeRn<double> {
using VBase = Composite<double, ublasRn<double> >;
using CTBase = CompositeCT<ublasRn<double> >;
using RnBase = CompositeRn<double>;
public:
CompositeCTRn() : VBase(), CTBase(), RnBase() {
VBase::_i1 = 1;
VBase::_i2.reset(new ublasRn<double>(3));
CTBase::_i1 = 2;
CTBase::_i2.reset(new ublasRn<double>(3));
RnBase::_i1 = 3;
RnBase::_i2.reset(new ublasRn<double>(3));
assert(3 == VBase::_i1);
assert(3 == CTBase::_i1);
assert(3 == RnBase::_i1);
assert(VBase::_i2.get() == CTBase::_i2.get());
assert(VBase::_i2.get() == RnBase::_i2.get());
RnBase::_i2.reset();
assert(!(VBase::_i2 || CTBase::_i2 || RnBase::_i2));
};
};
事实上,这正是 gcc 4.8、4.9、5.0 和 clang++ 3.5 上发生的情况。
#include <boost/numeric/ublas/vector.hpp>
namespace ublas = boost::numeric::ublas;
template <typename T = double> using ublasRn = ublas::vector<T>;
struct Base {
virtual ~Base() {}
};
template <typename T1, typename T2> class Composite : public Base {
protected:
template <typename, typename, typename> friend class Trajectory; // or make this public
using Type1 = T1;
using Type2 = T2;
Type1 _i1;
std::unique_ptr<Type2> _i2;
public:
Composite() = default;
};
template <typename T2> class CompositeCT : virtual public Composite<double, T2> {
public:
using Composite<double, T2>::Composite;
};
template <typename T1> class CompositeRn : virtual public Composite<T1, ublasRn<double> > {
public:
using Composite<T1, ublasRn<double> >::Composite;
};
class CompositeCTRn : public CompositeCT<ublasRn<double> >, public CompositeRn<double> {
using VBase = Composite<double, ublasRn<double> >;
using CTBase = CompositeCT<ublasRn<double> >;
using RnBase = CompositeRn<double>;
public:
CompositeCTRn() : VBase(), CTBase(), RnBase() {
VBase::_i1 = 1;
VBase::_i2.reset(new ublasRn<double>(3));
CTBase::_i1 = 2;
CTBase::_i2.reset(new ublasRn<double>(3));
RnBase::_i1 = 3;
RnBase::_i2.reset(new ublasRn<double>(3));
assert(3 == VBase::_i1);
assert(3 == CTBase::_i1);
assert(3 == RnBase::_i1);
assert(VBase::_i2.get() == CTBase::_i2.get());
assert(VBase::_i2.get() == RnBase::_i2.get());
RnBase::_i2.reset();
assert(!(VBase::_i2 || CTBase::_i2 || RnBase::_i2));
};
};
template <typename T1, typename T2, class TComposite> class Trajectory : public Base {
static_assert(std::is_same<T1, typename TComposite::Type1>::value, "mismatched composite");
static_assert(std::is_same<T2, typename TComposite::Type2>::value, "mismatched composite");
protected:
std::vector<std::unique_ptr<TComposite> > m_Trajectory;
public:
Trajectory() { checkType(); }
void checkType() const {
std::vector<TComposite> CI(1000);
}
virtual ~Trajectory() {}
};
#include <iostream>
int main() {
Trajectory<double, ublasRn<>, CompositeCTRn> T;
std::cout << 123 << "\n";
}
在 valgrind 下:
==15664== Memcheck, a memory error detector
==15664== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==15664== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==15664== Command: ./test
==15664==
123
==15664==
==15664== HEAP SUMMARY:
==15664== in use at exit: 0 bytes in 0 blocks
==15664== total heap usage: 6,001 allocs, 6,001 frees, 184,000 bytes allocated
==15664==
==15664== All heap blocks were freed -- no leaks are possible
==15664==
==15664== For counts of detected and suppressed errors, rerun with: -v
==15664== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
完全没问题。
备注
我用静态断言替换了类型检查(因为,为什么不呢?!)
template <typename T1, typename T2, class TComposite> class Trajectory : public Base {
static_assert(std::is_same<T1, typename TComposite::Type1>::value, "mismatched composite");
static_assert(std::is_same<T2, typename TComposite::Type2>::value, "mismatched composite");
总结/长话短说
我看过你的代码。除了一束漂亮的设计气味外,我真的没有发现任何问题。如果您需要更多帮助,则必须开始具体说明版本、标志、编译器、体系结构...
这是我之前 questions 之一的后续。我正在处理的问题在上述问题的表述中有详细解释。不幸的是,我无法提供展示问题的最小示例。
在这个问题中,我试图重新定义问题并提供一个最小的例子。下面示例中的代码执行并完成了它应该做的事情。但是,在前面 question 中介绍的稍微复杂的情况下,有时会导致运行时错误
dynamic_links(3941,0x7fff749a2310) malloc: *** error for object 0x61636f6c65720054: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
不幸的是,我只能在优化设置为 -O3
(也可能是 -O2
)时产生错误。这在使用调试工具时会产生问题。不幸的是,我无法通过 no/minimal 代码优化重现该问题。作为参考,我使用 gcc 4.9.1
.
在当前问题中,我想了解我正在使用的 class 继承机制的结构设计从动态内存分配的角度来看是否可能存在危险。请找到以下代码:
namespace ublas = boost::numeric::ublas;
template<typename TScalarType = double>
using ublasRn = ublas::vector<TScalarType>;
class Base
{
public:
virtual ~Base(void) = 0;
};
Base::~Base(void){}
template<typename T1, typename T2>
class Composite : public Base
{
protected:
T1 T1Instance;
std::unique_ptr<T2> u_T2Instance;
public:
Composite(){}
virtual ~Composite(void){}
const std::type_info& returnT1TypeID(void) const
{return typeid(T1);}
const std::type_info& returnT2TypeID(void) const
{return typeid(T2);}
};
template<typename T2>
class CompositeCT: virtual public Composite<double, T2>
{
public:
using Composite<double, T2>::Composite;
virtual ~CompositeCT(void)
{}
};
template<typename T1>
class CompositeRn: virtual public Composite<T1, ublasRn<double>>
{
public:
using Composite<T1, ublasRn<double>>::Composite;
virtual ~CompositeRn(void){}
};
class CompositeCTRn :
public CompositeCT<ublasRn<double>>,
public CompositeRn<double>
{
public:
CompositeCTRn(void):
Composite<double, ublasRn<double>>(),
CompositeCT<ublasRn<double>>(),
CompositeRn<double>()
{};
};
template<typename T1, typename T2, class TComposite>
class Trajectory: public Base
{
protected:
std::vector<std::unique_ptr<TComposite>> m_Trajectory;
public:
Trajectory(void)
{checkType();}
void checkType(void) const
{
TComposite CI;
if (
!(
CI.returnT1TypeID() == typeid(T1) &&
CI.returnT2TypeID() == typeid(T2)
)
)
throw std::runtime_error("Error");
}
virtual ~Trajectory(void){}
};
int main()
{
Trajectory<
double,
ublasRn<>,
CompositeCTRn
> T;
std::cout << 123 << std::endl;
}
注意。我正在使用外部库boost::ublas。我相信问题与 ublas
对象的动态内存分配机制有关的可能性不大。
嗯。我在想你真的在推动类型系统。我无法理解为什么你会想要这种令人震惊的类型层次结构:
在大约 60 行代码中造成了很大的破坏。我想不出 Liskov Substitution Principle 适用于此层次结构的合理情况。
I also note that this kind of emphasis on runtime polymorphism does seem to run counter to the design goals of C++ and uBlas in particular.
尤其是这一行(本质上声明了图表的中心):
class CompositeCTRn : public CompositeCT<ublasRn<double> >, public CompositeRn<double> {
这有效地声明了具有单个虚拟基的 class,即通过三个基的 "aliased":
using VBase = Composite<double, ublasRn<double> >;
using CTBase = CompositeCT<ublasRn<double> >;
using RnBase = CompositeRn<double>;
这意味着 应该 类型中只有一个 _i1
和一个 _i2
成员。这意味着在所有符合标准的编译器上,以下内容应该编译 运行 而不会触发任何断言或导致内存泄漏等:
class CompositeCTRn : public CompositeCT<ublasRn<double> >, public CompositeRn<double> {
using VBase = Composite<double, ublasRn<double> >;
using CTBase = CompositeCT<ublasRn<double> >;
using RnBase = CompositeRn<double>;
public:
CompositeCTRn() : VBase(), CTBase(), RnBase() {
VBase::_i1 = 1;
VBase::_i2.reset(new ublasRn<double>(3));
CTBase::_i1 = 2;
CTBase::_i2.reset(new ublasRn<double>(3));
RnBase::_i1 = 3;
RnBase::_i2.reset(new ublasRn<double>(3));
assert(3 == VBase::_i1);
assert(3 == CTBase::_i1);
assert(3 == RnBase::_i1);
assert(VBase::_i2.get() == CTBase::_i2.get());
assert(VBase::_i2.get() == RnBase::_i2.get());
RnBase::_i2.reset();
assert(!(VBase::_i2 || CTBase::_i2 || RnBase::_i2));
};
};
事实上,这正是 gcc 4.8、4.9、5.0 和 clang++ 3.5 上发生的情况。
#include <boost/numeric/ublas/vector.hpp>
namespace ublas = boost::numeric::ublas;
template <typename T = double> using ublasRn = ublas::vector<T>;
struct Base {
virtual ~Base() {}
};
template <typename T1, typename T2> class Composite : public Base {
protected:
template <typename, typename, typename> friend class Trajectory; // or make this public
using Type1 = T1;
using Type2 = T2;
Type1 _i1;
std::unique_ptr<Type2> _i2;
public:
Composite() = default;
};
template <typename T2> class CompositeCT : virtual public Composite<double, T2> {
public:
using Composite<double, T2>::Composite;
};
template <typename T1> class CompositeRn : virtual public Composite<T1, ublasRn<double> > {
public:
using Composite<T1, ublasRn<double> >::Composite;
};
class CompositeCTRn : public CompositeCT<ublasRn<double> >, public CompositeRn<double> {
using VBase = Composite<double, ublasRn<double> >;
using CTBase = CompositeCT<ublasRn<double> >;
using RnBase = CompositeRn<double>;
public:
CompositeCTRn() : VBase(), CTBase(), RnBase() {
VBase::_i1 = 1;
VBase::_i2.reset(new ublasRn<double>(3));
CTBase::_i1 = 2;
CTBase::_i2.reset(new ublasRn<double>(3));
RnBase::_i1 = 3;
RnBase::_i2.reset(new ublasRn<double>(3));
assert(3 == VBase::_i1);
assert(3 == CTBase::_i1);
assert(3 == RnBase::_i1);
assert(VBase::_i2.get() == CTBase::_i2.get());
assert(VBase::_i2.get() == RnBase::_i2.get());
RnBase::_i2.reset();
assert(!(VBase::_i2 || CTBase::_i2 || RnBase::_i2));
};
};
template <typename T1, typename T2, class TComposite> class Trajectory : public Base {
static_assert(std::is_same<T1, typename TComposite::Type1>::value, "mismatched composite");
static_assert(std::is_same<T2, typename TComposite::Type2>::value, "mismatched composite");
protected:
std::vector<std::unique_ptr<TComposite> > m_Trajectory;
public:
Trajectory() { checkType(); }
void checkType() const {
std::vector<TComposite> CI(1000);
}
virtual ~Trajectory() {}
};
#include <iostream>
int main() {
Trajectory<double, ublasRn<>, CompositeCTRn> T;
std::cout << 123 << "\n";
}
在 valgrind 下:
==15664== Memcheck, a memory error detector
==15664== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==15664== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==15664== Command: ./test
==15664==
123
==15664==
==15664== HEAP SUMMARY:
==15664== in use at exit: 0 bytes in 0 blocks
==15664== total heap usage: 6,001 allocs, 6,001 frees, 184,000 bytes allocated
==15664==
==15664== All heap blocks were freed -- no leaks are possible
==15664==
==15664== For counts of detected and suppressed errors, rerun with: -v
==15664== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
完全没问题。
备注
我用静态断言替换了类型检查(因为,为什么不呢?!)
template <typename T1, typename T2, class TComposite> class Trajectory : public Base { static_assert(std::is_same<T1, typename TComposite::Type1>::value, "mismatched composite"); static_assert(std::is_same<T2, typename TComposite::Type2>::value, "mismatched composite");
总结/长话短说
我看过你的代码。除了一束漂亮的设计气味外,我真的没有发现任何问题。如果您需要更多帮助,则必须开始具体说明版本、标志、编译器、体系结构...