我最近对std::allocator
感兴趣,认为它可能解决了我对C ++代码的一些设计决定的问题。
现在我已经阅读了一些关于它的文档,观看了一些视频,比如Andrei Alexandrescu's one at CppCon 2015,我现在基本上明白我不应该使用它们,因为它们并非如此旨在以我认为分配器可能工作的方式工作。
话虽如此,在实现这一点之前,我编写了一些测试代码,以了解std::allocator
的自定义子类如何工作。
显然,没有像预期的那样工作...... :)
所以问题不在于如何在C ++中使用分配器,而是我只是想知道为什么我的测试代码(下面提供)不起作用。
不是因为我想使用自定义分配器。只是好奇地看到确切的原因...
typedef std::basic_string< char, std::char_traits< char >, TestAllocator< char > > TestString;
int main( void )
{
TestString s1( "hello" );
TestString s2( s1 );
s1 += ", world";
std::vector< int, TestAllocator< int > > v;
v.push_back( 42 );
return 0;
}
本问题末尾提供了TestAllocator
的完整代码。
我只是将我的自定义分配器与std::basic_string
和std::vector
一起使用。
使用std::basic_string
,我可以看到实际创建了我的分配器实例,但没有调用任何方法...
所以看起来它根本就没用过。
但是使用std::vector
,我实际上正在调用我自己的allocate
方法。
那么为什么会有区别?
我尝试过不同的编译器和C ++版本。
看起来像旧的GCC版本,使用C ++ 98,在allocate
类型上调用TestString
,而不是在C ++ 11及更高版本上调用新版本。
Clang也没有打电话给allocate
。
所以只是好奇地看到有关这些不同行为的解释。
分配器代码:
template< typename _T_ >
struct TestAllocator
{
public:
typedef _T_ value_type;
typedef _T_ * pointer;
typedef const _T_ * const_pointer;
typedef _T_ & reference;
typedef const _T_ & const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef std::true_type propagate_on_container_move_assignment;
typedef std::true_type is_always_equal;
template< class _U_ >
struct rebind
{
typedef TestAllocator< _U_ > other;
};
TestAllocator( void ) noexcept
{
std::cout << "CTOR" << std::endl;
}
TestAllocator( const TestAllocator & other ) noexcept
{
( void )other;
std::cout << "CCTOR" << std::endl;
}
template< class _U_ >
TestAllocator( const TestAllocator< _U_ > & other ) noexcept
{
( void )other;
std::cout << "CCTOR" << std::endl;
}
~TestAllocator( void )
{
std::cout << "DTOR" << std::endl;
}
pointer address( reference x ) const noexcept
{
return std::addressof( x );
}
pointer allocate( size_type n, std::allocator< void >::const_pointer hint = 0 )
{
pointer p;
( void )hint;
std::cout << "allocate" << std::endl;
p = new _T_[ n ]();
if( p == nullptr )
{
throw std::bad_alloc() ;
}
return p;
}
void deallocate( _T_ * p, std::size_t n )
{
( void )n;
std::cout << "deallocate" << std::endl;
delete[] p;
}
const_pointer address( const_reference x ) const noexcept
{
return std::addressof( x );
}
size_type max_size() const noexcept
{
return size_type( ~0 ) / sizeof( _T_ );
}
void construct( pointer p, const_reference val )
{
( void )p;
( void )val;
std::cout << "construct" << std::endl;
}
void destroy( pointer p )
{
( void )p;
std::cout << "destroy" << std::endl;
}
};
template< class _T1_, class _T2_ >
bool operator ==( const TestAllocator< _T1_ > & lhs, const TestAllocator< _T2_ > & rhs ) noexcept
{
( void )lhs;
( void )rhs;
return true;
}
template< class _T1_, class _T2_ >
bool operator !=( const TestAllocator< _T1_ > & lhs, const TestAllocator< _T2_ > & rhs ) noexcept
{
( void )lhs;
( void )rhs;
return false;
}
答案 0 :(得分:5)
std::basic_string
可以使用小缓冲区优化 (在字符串上下文中称为SBO或SSO)实现 - 这意味着它在内部存储一个小的避免分配小字符串的缓冲区。这很可能是您的分配器未被使用的原因。
尝试将"hello"
更改为更长的字符串(超过32个字符),并且可能会调用allocate
。
另请注意,C ++ 11标准禁止以COW (copy-on-write)方式实现std::string
- 此问题中的更多信息:{{3} }
标准禁止std::vector
使用小型缓冲区优化:可以在此问题中找到更多信息:"Legality of COW std::string implementation in C++11"。