我有一个类使用boost :: aligned存储来静态分配对象内存,然后稍后初始化对象。但是,当我尝试在函数调用后返回对象时,一个测试程序中的崩溃似乎显示成员的析构函数在初始化对象上调用,在这种情况下是std :: set。然后,只有数据在分配给返回站点的新对象时才会被复制,就好像该对象是POD类型一样。这意味着下次我尝试访问分配的对象时,它会因内存访问错误而失败。
更具体地说,我正在实现一个名为StaticVector的类,它是std :: vector的一个子集,它试图紧密匹配Boost.Array加上大小跟踪和编译时固定容量。
我的测试程序benchStaticVector运行如下:
此次崩溃的原因是什么,我该如何解决?
完整代码和方便的CMakeLists.txt文件可在以下位置找到: https://github.com/ahundt/Boost.StaticVector
benchStaticVector.cpp:
// benchmark based on: http://cpp-next.com/archive/2010/10/howards-stl-move-semantics-benchmark/
#include "StaticVector.hpp"
#include <vector>
#include <iostream>
#include <time.h>
#include <set>
#include <algorithm>
#include <exception>
const unsigned N = 3;
extern bool some_test;
template<typename T>
T get_set(std::size_t)
{
T s;
for (std::size_t i = 0; i < N; ++i)
while (!s.insert(std::rand()).second)
;
if (some_test)
return s;
return T();
}
template<typename T>
T generate()
{
T v;
for (std::size_t i = 0; i < N; ++i)
v.push_back(get_set<typename T::value_type>(i));
if (some_test)
return v;
return T();
}
template<typename T>
float time_it()
{
clock_t t1, t2, t3, t4;
clock_t t0 = clock();
{
T v(generate<T>());
}
return (float)((t4-t0)/(double)CLOCKS_PER_SEC);
}
int main()
{
try {
std::cout << "N = " << N << "\n\n";
std::cout << "StaticVector Benchmark:\n";
float t = time_it<boost::StaticVector<std::set<std::size_t>,N > >();
std::cout << "Total time = " << t << "\n\n";
std::cout << "Vector: Benchmark\n";
t = time_it<std::vector<std::set<std::size_t> > >();
std::cout << "Total time = " << t << '\n';
}catch(std::exception e){
std::cout << e.what();
}
}
bool some_test = true;
以下是StaticVector.hpp中的类:
/**
* @file StaticVector.hpp
* @date Feb 23, 2011
* @brief Vector style class with fixed capacity.
*
* The following code declares class StaticVector,
* an STL container (as wrapper) for a statically allocated vector with a constant size limit.
* StaticVector is not accepted as part of boost.
*
* (C) Carnegie Mellon University 2011
* @author Andrew Hundt <ahundt@cmu.edu>
*
* based on boost::array
* The original author site of boost::array is at: http://www.josuttis.com/
* (C) Copyright Nicolai M. Josuttis 2001.
*
* Distributed under the Boost Software License, Version 1.0. (See
* accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* 09 Oct 2011 - (ath) eliminated construction of objects on initialization of StaticVector
* 23 Feb 2011 - (ath) converted to boost::StaticVector
* 28 Dec 2010 - (mtc) Added cbegin and cend (and crbegin and crend) for C++Ox compatibility.
* 10 Mar 2010 - (mtc) fill method added, matching resolution of the standard library working group.
* See <http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#776> or Trac issue #3168
* Eventually, we should remove "assign" which is now a synonym for "fill" (Marshall Clow)
* 10 Mar 2010 - added workaround for SUNCC and !STLPort [trac #3893] (Marshall Clow)
* 29 Jan 2004 - c_array() added, BOOST_NO_PRIVATE_IN_AGGREGATE removed (Nico Josuttis)
* 23 Aug 2002 - fix for Non-MSVC compilers combined with MSVC libraries.
* 05 Aug 2001 - minor update (Nico Josuttis)
* 20 Jan 2001 - STLport fix (Beman Dawes)
* 29 Sep 2000 - Initial Revision (Nico Josuttis)
*
* Jan 29, 2004
*/
#ifndef BOOST_STATIC_VECTOR_HPP
#define BOOST_STATIC_VECTOR_HPP
#include <boost/config.hpp>
#include <boost/detail/workaround.hpp>
#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
# pragma warning(push)
# pragma warning(disable:4996) // 'std::equal': Function call with parameters that may be unsafe
# pragma warning(disable:4510) // boost::StaticVector<T,N>' : default constructor could not be generated
# pragma warning(disable:4610) // warning C4610: class 'boost::StaticVector<T,N>' can never be instantiated - user defined constructor required
#endif
#include <cstddef>
#include <stdexcept>
#include <boost/assert.hpp>
#if ((BOOST_VERSION / 100) % 1000) > 44
#include <boost/swap.hpp>
#endif
// Handles broken standard libraries better than <iterator>
#include <boost/detail/iterator.hpp>
#include <boost/throw_exception.hpp>
#include <algorithm>
// FIXES for broken compilers
#include <boost/config.hpp>
// Selection of types for internal storage
#include <boost/integer.hpp>
#include <boost/type_traits/alignment_of.hpp>
#include <boost/type_traits/aligned_storage.hpp>
#include <boost/type_traits/has_trivial_destructor.hpp>
namespace boost {
template<class T, std::size_t N>
class StaticVector {
public:
// type definitions
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T* iterator;
typedef const T* const_iterator;
typedef T& reference;
typedef const T& const_reference;
typedef typename boost::aligned_storage<
sizeof(T),
boost::alignment_of<T>::value
>::type aligned_storage;
typedef typename boost::uint_value_t<N>::least size_type;
typedef std::size_t max_size_type;
typedef std::ptrdiff_t difference_type;
private:
size_type m_size; // fastest type that can accomodate N
aligned_storage elems[N]; // fixed-size array of memory aligned elements of type T
public:
// iterator support
iterator begin() { return reinterpret_cast<iterator>(elems); }
const_iterator begin() const { return reinterpret_cast<const_iterator>(elems); }
const_iterator cbegin() const { return reinterpret_cast<const_iterator>(elems); }
iterator end() { return to_object(m_size); }
const_iterator end() const { return to_object(m_size); }
const_iterator cend() const { return to_object(m_size); }
// reverse iterator support
#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) && !defined(BOOST_MSVC_STD_ITERATOR) && !defined(BOOST_NO_STD_ITERATOR_TRAITS)
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
#elif defined(_MSC_VER) && (_MSC_VER == 1300) && defined(BOOST_DINKUMWARE_STDLIB) && (BOOST_DINKUMWARE_STDLIB == 310)
// workaround for broken reverse_iterator in VC7
typedef std::reverse_iterator<std::_Ptrit<value_type, difference_type, iterator,
reference, iterator, reference> > reverse_iterator;
typedef std::reverse_iterator<std::_Ptrit<value_type, difference_type, const_iterator,
const_reference, iterator, reference> > const_reverse_iterator;
#elif defined(_RWSTD_NO_CLASS_PARTIAL_SPEC)
typedef std::reverse_iterator<iterator, std::random_access_iterator_tag,
value_type, reference, iterator, difference_type> reverse_iterator;
typedef std::reverse_iterator<const_iterator, std::random_access_iterator_tag,
value_type, const_reference, const_iterator, difference_type> const_reverse_iterator;
#else
// workaround for broken reverse_iterator implementations
typedef std::reverse_iterator<iterator,T> reverse_iterator;
typedef std::reverse_iterator<const_iterator,T> const_reverse_iterator;
#endif
reverse_iterator rbegin() { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const {
return const_reverse_iterator(end());
}
const_reverse_iterator crbegin() const {
return const_reverse_iterator(end());
}
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator rend() const {
return const_reverse_iterator(begin());
}
const_reverse_iterator crend() const {
return const_reverse_iterator(begin());
}
StaticVector():m_size(0){}
StaticVector(size_type n, const_reference value):
m_size(n)
{
insert(begin(),n,value);
}
template<typename InputIterator>
StaticVector(InputIterator first, InputIterator last):
m_size(last-first)
{
capacitycheck(size());
std::copy(first,last,begin());
}
template<std::size_t SizeRHS>
StaticVector(const StaticVector<T,SizeRHS>& rhs):
m_size(rhs.size())
{
capacitycheck(rhs.size());
std::copy(rhs.begin(),rhs.end(),begin());
}
~StaticVector(){
destroy_array(::boost::has_trivial_destructor<T>());
m_size=0;
}
void push_back (const_reference x){
capacitycheck(size()+1);
new (to_object(size())) T(x);
m_size++;
}
void pop_back(){
if(!empty()){
to_object(size()-1)->~T();
m_size--;
} else {
boost::throw_exception( std::out_of_range("StaticVector<> pop called on empty container."));
}
}
iterator insert(iterator pos, const_reference x){
capacitycheck(size()+1);
std::copy_backward(pos,end(),end()+1);
*pos = x;
m_size++;
return pos;
}
void insert(iterator pos, max_size_type n, const_reference x){
capacitycheck(size()+n);
std::copy_backward(pos,end(),end()+n);
std::fill(pos,pos+n,x);
m_size+=n;
}
template <typename InputIterator>
void insert(iterator pos, InputIterator first, InputIterator last){
max_size_type n = last - first;
capacitycheck(size()+n);
std::copy_backward(pos,end(),end()+n);
std::copy(first,last,pos);
}
iterator erase(iterator pos){
rangecheck(pos-begin());
pos->~T();
std::copy(pos+1,end(),pos);
m_size--;
return pos;
}
iterator erase(iterator first, iterator last){
std::ptrdiff_t n = last-first;
if (n>0) {
rangecheck(size()-n);
for(iterator it = first; it!=last; it++){
it->~T();
}
std::copy(last,end(),first);
m_size -= n;
}
return first;
}
void clear(){
erase(begin(),end());
}
void resize(max_size_type n, const_reference t = T() ){
capacitycheck(n);
if(n - m_size > 0){
std::fill_n(end(), n-m_size, t);
} else {
erase(begin()+n,end());
}
m_size = n;
}
void reserve(max_size_type n){
capacitycheck(n);
}
// operator[]
reference operator[](max_size_type i)
{
BOOST_ASSERT( i < N || i < size() && "StaticVector<>: out of range" );
return *to_object(i);
}
const_reference operator[](max_size_type i) const
{
BOOST_ASSERT( i < N || i < size() && "StaticVector<>: out of range" );
return *to_object(i);
}
// at() with range check
reference at(max_size_type i) { rangecheck(i); return *to_object(i); }
const_reference at(max_size_type i) const { rangecheck(i); return *to_object(i); }
// front() and back()
reference front()
{
return begin();
}
const_reference front() const
{
return begin();
}
reference back()
{
return *to_object(size()-1);
}
const_reference back() const
{
return *to_object(size()-1);
}
// capacity is constant, size varies
max_size_type size() const { return m_size; }
static max_size_type capacity() { return N; }
bool empty() { return m_size == 0; }
static max_size_type max_size() { return N; }
enum { static_size = N };
// swap (note: linear complexity)
void swap (StaticVector<T,N>& y) {
#if ((BOOST_VERSION / 100) % 1000) > 44
for (size_type i = 0; i < N; ++i)
boost::swap(*to_object(i),*y.to_object(i));
boost::swap(m_size,y.m_size);
#else
std::swap_ranges(begin(),end(),y.begin());
std::swap(m_size,y.m_size);
#endif
}
// direct access to data (read-only)
const_pointer data() const { return elems; }
pointer data() { return elems; }
// use array as C array (direct read/write access to data)
pointer c_array() { return elems; }
// assignment with type conversion
template <typename T2>
StaticVector<T,N>& operator= (const StaticVector<T2,N>& rhs) {
std::copy(rhs.begin(),rhs.end(), begin());
m_size = rhs.size();
return *this;
}
// assign one value to all elements
void assign (const T& value) { fill ( value ); } // A synonym for fill
void fill (const T& value)
{
std::fill_n(begin(),size(),value);
}
// check range (may be private because it is static)
void rangecheck (max_size_type i) const {
if (i >= size()) {
std::out_of_range e("StaticVector<>: index out of range");
boost::throw_exception(e);
}
}
// check range (may be private because it is static)
void capacitycheck (max_size_type i) const {
if (i > capacity()) {
std::out_of_range e("StaticVector<>: index out of capacity");
boost::throw_exception(e);
}
}
private:
inline const_pointer to_object(size_type index) const {
return reinterpret_cast<const_pointer>(elems+index);
}
inline pointer to_object(size_type index) {
return reinterpret_cast<pointer>(elems+index);
}
// T has a trivial destructor, do nothing
inline void destroy_array(const boost::true_type&) {}
// T has a destructor, destroy each object
inline void destroy_array(const boost::false_type&) {
for(iterator first = begin(); first != end(); ++first) {
first->~T();
}
}
}; // class StaticVector
#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)
template< class T >
class StaticVector< T, 0 > {
public:
// type definitions
typedef T value_type;
typedef T* iterator;
typedef const T* const_iterator;
typedef T& reference;
typedef const T& const_reference;
typedef typename boost::aligned_storage<
sizeof(T),
boost::alignment_of<T>::value
>::type aligned_storage;
typedef typename boost::uint_value_t<0>::least size_type;
typedef std::size_t max_size_type;
typedef std::ptrdiff_t difference_type;
// iterator support
iterator begin() { return iterator( reinterpret_cast< T * >( this ) ); }
const_iterator begin() const { return const_iterator( reinterpret_cast< const T * >( this ) ); }
const_iterator cbegin() const { return const_iterator( reinterpret_cast< const T * >( this ) ); }
iterator end() { return begin(); }
const_iterator end() const { return begin(); }
const_iterator cend() const { return cbegin(); }
// reverse iterator support
#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) && !defined(BOOST_MSVC_STD_ITERATOR) && !defined(BOOST_NO_STD_ITERATOR_TRAITS)
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
#elif defined(_MSC_VER) && (_MSC_VER == 1300) && defined(BOOST_DINKUMWARE_STDLIB) && (BOOST_DINKUMWARE_STDLIB == 310)
// workaround for broken reverse_iterator in VC7
typedef std::reverse_iterator<std::_Ptrit<value_type, difference_type, iterator,
reference, iterator, reference> > reverse_iterator;
typedef std::reverse_iterator<std::_Ptrit<value_type, difference_type, const_iterator,
const_reference, iterator, reference> > const_reverse_iterator;
#elif defined(_RWSTD_NO_CLASS_PARTIAL_SPEC)
typedef std::reverse_iterator<iterator, std::random_access_iterator_tag,
value_type, reference, iterator, difference_type> reverse_iterator;
typedef std::reverse_iterator<const_iterator, std::random_access_iterator_tag,
value_type, const_reference, const_iterator, difference_type> const_reverse_iterator;
#else
// workaround for broken reverse_iterator implementations
typedef std::reverse_iterator<iterator,T> reverse_iterator;
typedef std::reverse_iterator<const_iterator,T> const_reverse_iterator;
#endif
reverse_iterator rbegin() { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const {
return const_reverse_iterator(end());
}
const_reverse_iterator crbegin() const {
return const_reverse_iterator(end());
}
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator rend() const {
return const_reverse_iterator(begin());
}
const_reverse_iterator crend() const {
return const_reverse_iterator(begin());
}
void push_back (const_reference x){
failed_rangecheck();
}
void pop_back(){
failed_rangecheck();
}
iterator insert(iterator pos, const_reference x){
return failed_rangecheck();
}
void insert(iterator pos, max_size_type n, const_reference x){
failed_rangecheck();
}
template <typename InputIterator>
void insert(iterator pos, InputIterator first, InputIterator last){
failed_rangecheck();
}
iterator erase(iterator pos){
return failed_rangecheck();
}
iterator erase(iterator first, iterator last){
return failed_rangecheck();
}
void clear(){
}
void resize(max_size_type n, const_reference t = T() ){
failed_rangecheck();
}
void reserve(size_type n){
failed_rangecheck();
}
// operator[]
reference operator[](max_size_type /*i*/)
{
return failed_rangecheck();
}
const_reference operator[](max_size_type /*i*/) const
{
return failed_rangecheck();
}
// at() with range check
reference at(max_size_type /*i*/) { return failed_rangecheck(); }
const_reference at(max_size_type /*i*/) const { return failed_rangecheck(); }
// front() and back()
reference front()
{
return failed_rangecheck();
}
const_reference front() const
{
return failed_rangecheck();
}
reference back()
{
return failed_rangecheck();
}
const_reference back() const
{
return failed_rangecheck();
}
// size is constant
static max_size_type size() { return 0; }
static bool empty() { return true; }
static max_size_type max_size() { return 0; }
enum { static_size = 0 };
void swap (StaticVector<T,0>& /*y*/) {
}
// direct access to data (read-only)
const T* data() const { return 0; }
T* data() { return 0; }
// use array as C array (direct read/write access to data)
T* c_array() { return 0; }
// assignment with type conversion
template <typename T2>
StaticVector<T,0>& operator= (const StaticVector<T2,0>& ) {
return *this;
}
// assign one value to all elements
void assign (const T& value) { fill ( value ); }
void fill (const T& ) {}
// check range (may be private because it is static)
static reference failed_rangecheck () {
std::out_of_range e("attempt to access element of an empty StaticVector");
boost::throw_exception(e);
#if defined(BOOST_NO_EXCEPTIONS) || !defined(BOOST_MSVC)
//
// We need to return something here to keep
// some compilers happy: however we will never
// actually get here....
//
static T placeholder;
return placeholder;
#endif
}
};
#endif
// comparisons
template<class T, std::size_t N>
bool operator== (const StaticVector<T,N>& x, const StaticVector<T,N>& y) {
return std::equal(x.begin(), x.end(), y.begin());
}
template<class T, std::size_t N>
bool operator< (const StaticVector<T,N>& x, const StaticVector<T,N>& y) {
return std::lexicographical_compare(x.begin(),x.end(),y.begin(),y.end());
}
template<class T, std::size_t N>
bool operator!= (const StaticVector<T,N>& x, const StaticVector<T,N>& y) {
return !(x==y);
}
template<class T, std::size_t N>
bool operator> (const StaticVector<T,N>& x, const StaticVector<T,N>& y) {
return y<x;
}
template<class T, std::size_t N>
bool operator<= (const StaticVector<T,N>& x, const StaticVector<T,N>& y) {
return !(y<x);
}
template<class T, std::size_t N>
bool operator>= (const StaticVector<T,N>& x, const StaticVector<T,N>& y) {
return !(x<y);
}
// global swap()
template<class T, std::size_t N>
inline void swap (StaticVector<T,N>& x, StaticVector<T,N>& y) {
x.swap(y);
}
#if defined(__SUNPRO_CC)
// Trac ticket #4757; the Sun Solaris compiler can't handle
// syntax like 'T(&get_c_array(boost::StaticVector<T,N>& arg))[N]'
//
// We can't just use this for all compilers, because the
// borland compilers can't handle this form.
namespace detail {
template <typename T, std::size_t N> struct c_array
{
typedef T type[N];
};
}
// Specific for boost::StaticVector: simply returns its elems data member.
template <typename T, std::size_t N>
typename detail::c_array<T,N>::type& get_c_array(boost::StaticVector<T,N>& arg)
{
return arg.elems;
}
// Specific for boost::StaticVector: simply returns its elems data member.
template <typename T, std::size_t N>
typename const detail::c_array<T,N>::type& get_c_array(const boost::StaticVector<T,N>& arg)
{
return arg.elems;
}
#else
// Specific for boost::StaticVector: simply returns its elems data member.
template <typename T, std::size_t N>
T(&get_c_array(boost::StaticVector<T,N>& arg))[N]
{
return arg.elems;
}
// Const version.
template <typename T, std::size_t N>
const T(&get_c_array(const boost::StaticVector<T,N>& arg))[N]
{
return arg.elems;
}
#endif
#if 0
// Overload for std::array, assuming that std::array will have
// explicit conversion functions as discussed at the WG21 meeting
// in Summit, March 2009.
template <typename T, std::size_t N>
T(&get_c_array(std::array<T,N>& arg))[N]
{
return static_cast<T(&)[N]>(arg);
}
// Const version.
template <typename T, std::size_t N>
const T(&get_c_array(const std::array<T,N>& arg))[N]
{
return static_cast<T(&)[N]>(arg);
}
#endif
} /* namespace boost */
#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
# pragma warning(pop)
#endif
#endif /*BOOST_STATIC_VECTOR_HPP*/
答案 0 :(得分:4)
你打破了rule of three。你需要一个复制构造函数。请注意,使用const StaticVector<T,SizeRHS>&
的构造函数不是复制构造函数,因为它是一个模板。因此,当复制StaticVector
时(例如,当从函数返回时),使用编译器生成的复制构造函数,复制存储(没关系,它是POD)。在销毁时,析构函数访问存储,就像它包含活动对象一样,这很糟糕。
答案 1 :(得分:1)
boost::aligned_storage
实际上是POD类型(C ++ 11要求),并且不知道存储在其中的类型。它只是提供“存储”。如果要将对象从一个存储复制到另一个存储,则必须自己执行复制。
您是否考虑过基于堆栈存储实现自己的分配器,并重用标准容器?