我有一个使用需要使用LocalAlloc()
的API的Visual Studio 2008 Windows Mobile 6 C ++应用程序。为了让我的生活更轻松,我创建了一个在内部使用LocalAlloc()的标准分配器的实现:
/// Standard library allocator implementation using LocalAlloc and LocalReAlloc
/// to create a dynamically-sized array.
/// Memory allocated by this allocator is never deallocated. That is up to the
/// user.
template< class T, int max_allocations >
class LocalAllocator
{
public:
typedef T value_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
pointer address( reference r ) const { return &r; };
const_pointer address( const_reference r ) const { return &r; };
LocalAllocator() throw() : c_( NULL )
{
};
/// Attempt to allocate a block of storage with enough space for n elements
/// of type T. n>=1 && n<=max_allocations.
/// If memory cannot be allocated, a std::bad_alloc() exception is thrown.
pointer allocate( size_type n, const void* /*hint*/ = 0 )
{
if( NULL == c_ )
{
c_ = LocalAlloc( LPTR, sizeof( T ) * n );
}
else
{
HLOCAL c = LocalReAlloc( c_, sizeof( T ) * n, LHND );
if( NULL == c )
LocalFree( c_ );
c_ = c;
}
if( NULL == c_ )
throw std::bad_alloc();
return reinterpret_cast< T* >( c_ );
};
/// Normally, this would release a block of previously allocated storage.
/// Since that's not what we want, this function does nothing.
void deallocate( pointer /*p*/, size_type /*n*/ )
{
// no deallocation is performed. that is up to the user.
};
/// maximum number of elements that can be allocated
size_type max_size() const throw() { return max_allocations; };
private:
/// current allocation point
HLOCAL c_;
}; // class LocalAllocator
我的应用程序在std :: vector&lt;&gt;
中使用该分配器实现#define MAX_DIRECTORY_LISTING 512
std::vector< WIN32_FIND_DATA,
LocalAllocator< WIN32_FIND_DATA, MAX_DIRECTORY_LISTING > > file_list;
WIN32_FIND_DATA find_data = { 0 };
HANDLE find_file = ::FindFirstFile( folder.c_str(), &find_data );
if( NULL != find_file )
{
do
{
// access violation here on the 257th item.
file_list.push_back( find_data );
} while ( ::FindNextFile( find_file, &find_data ) );
::FindClose( find_file );
}
// data submitted to the API that requires LocalAlloc()'d array of WIN32_FIND_DATA structures
SubmitData( &file_list.front() );
在添加到矢量&lt;&gt;的第257项上,应用程序因访问冲突而崩溃:
Data Abort: Thread=8e1b0400 Proc=8031c1b0 'rapiclnt'
AKY=00008001 PC=03f9e3c8(coredll.dll+0x000543c8) RA=03f9ff04(coredll.dll+0x00055f04) BVA=21ae0020 FSR=00000007
First-chance exception at 0x03f9e3c8 in rapiclnt.exe: 0xC0000005: Access violation reading location 0x01ae0020.
调用{p> LocalAllocator::allocate
,n=512
成功,LocalReAlloc()
成功。实际的访问冲突异常发生在std :: vector&lt;&gt;中LocalAllocator::allocate
电话后的代码:
0x03f9e3c8
0x03f9ff04
> MyLib.dll!stlp_std::priv::__copy_trivial(const void* __first = 0x01ae0020, const void* __last = 0x01b03020, void* __result = 0x01b10020) Line: 224, Byte Offsets: 0x3c C++
MyLib.dll!stlp_std::vector<_WIN32_FIND_DATAW,LocalAllocator<_WIN32_FIND_DATAW,512> >::_M_insert_overflow(_WIN32_FIND_DATAW* __pos = 0x01b03020, _WIN32_FIND_DATAW& __x = {...}, stlp_std::__true_type& __formal = {...}, unsigned int __fill_len = 1, bool __atend = true) Line: 112, Byte Offsets: 0x5c C++
MyLib.dll!stlp_std::vector<_WIN32_FIND_DATAW,LocalAllocator<_WIN32_FIND_DATAW,512> >::push_back(_WIN32_FIND_DATAW& __x = {...}) Line: 388, Byte Offsets: 0xa0 C++
MyLib.dll!Foo(unsigned long int cbInput = 16, unsigned char* pInput = 0x01a45620, unsigned long int* pcbOutput = 0x1dabfbbc, unsigned char** ppOutput = 0x1dabfbc0, IRAPIStream* __formal = 0x00000000) Line: 66, Byte Offsets: 0x1e4 C++
如果有人能够指出我可能做错了什么,我将不胜感激。
谢谢, PaulH
答案 0 :(得分:2)
我不确定这是怎么回事。分配每个下一个块时,释放当前使用的块。所以指向那里的所有指针都变得无效。我怀疑这是矢量对分配器的期望。
答案 1 :(得分:2)
我首先认为您的问题是参数LHND
到LocalReAlloc()
- 您通常不应该将该参数传递给该函数。
实际问题是你甚至不应该调用该函数。 vector
实现重新分配自己的内存。 C ++标准分配器不提供重新分配。
你应该实现:
template<typename T> class LocalAllocator {
...
pointer allocate(sizetype s)
{
pointer p = reinterpret_cast<pointer>(LocalAlloc(LPTR, s * sizeof(T)));
if (NULL == p)
throw std::bad_alloc();
return p;
}
void deallocate(pointer p, sizetype)
{
LocalFree(reinterpret_cast<LHND>(p));
}
...
}
类似的东西应该有用。
您无需跟踪指针 - 它会在致电deallocate()
时提供给您,这是您的API客户端vector<T>
实施的责任
答案 2 :(得分:1)
要展开Igor Krivokon's answer,每次调用allocate()
可能会使之前调用allocate()
所返回的所有指针无效。这不是分配器应该如何工作,并且很可能导致未定义的行为:
例如,当向量需要分配新的更大内存时,它会将数据从“旧”内存复制到allocate()
返回的“新”内存。然后它将销毁“旧”内存中的所有对象并释放它。
如果 old 和 new 现在是相同的内存位置,则复制“new”对象的构造以及以下对“旧”对象的破坏将全部搞乱起来。另一方面,如果 old 不再有效(因为它已重新分配),该向量将访问无效的内存。
还有一点:分配器必须是可复制构造的。如果从分配器构造副本,然后两者都用于分配更多元素,则将在同一指针上调用LocalReAlloc()
两次。因此,您需要提供一个避免此类问题的自定义复制构造函数。