Bra*_*don 18 c++ inheritance c++11
我创建了自己的分配器,如下所示:
template<typename T>
class BasicAllocator
{
public:
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;
typedef T value_type;
BasicAllocator() throw() {};
BasicAllocator (const BasicAllocator& other) throw() {};
template<typename U>
BasicAllocator (const BasicAllocator<U>& other) throw() {};
template<typename U>
BasicAllocator& operator = (const BasicAllocator<U>& other) {return *this;}
BasicAllocator<T>& operator = (const BasicAllocator& other) {return *this;}
~BasicAllocator() {}
pointer address (reference value) const {return &value;}
const_pointer address (const_reference value) const {return &value;}
pointer allocate (size_type n, const void* hint = 0) {return static_cast<pointer> (::operator new (n * sizeof (value_type) ) );}
void deallocate (void* ptr, size_type n) {::operator delete (static_cast<T*> (ptr) );}
template<typename U, typename... Args>
void construct (U* ptr, Args&& ... args) {::new (static_cast<void*> (ptr) ) U (std::forward<Args> (args)...);}
void construct (pointer ptr, const T& val) {new (static_cast<T*> (ptr) ) T (val);}
template<typename U>
void destroy (U* ptr) {ptr->~U();}
void destroy (pointer ptr) {ptr->~T();}
size_type max_size() const {return std::numeric_limits<std::size_t>::max() / sizeof (T);} /**return std::size_t(-1);**/
template<typename U>
struct rebind
{
typedef BasicAllocator<U> other;
};
};
Run Code Online (Sandbox Code Playgroud)
但我想知道为什么我不应该继承std::allocator
.是因为它没有虚拟析构函数吗?我看过很多帖子说应该创建自己的而不是继承.我明白为什么我们不应该继承std::string
,std::vector
但继承有什么问题std::allocator
?
我可以继承上面的课吗?或者我需要一个虚拟析构函数来做到这一点?
为什么?
Cas*_*sey 30
很多人都会在这个帖子中发帖,你不应该继承,std::allocator
因为它没有虚拟的析构函数.他们将讨论多态性以及通过指针到基类的切片和删除,这些都不是分配器要求所允许的,如标准的第17.6.3.5节[allocator.requirements]中所述.直到有人证明某个类来自std::allocator
不能满足其中一个要求,这是简单的货物崇拜心态.
也就是说,没有理由从std::allocator
C++ 11中衍生出来.C++ 11对分配器的大修引入了特征模板,std::allocator_traits
使其位于分配器及其用户之间,并通过模板元编程为许多所需特性提供合理的默认值.C++ 11中的最小分配器可以简单如下:
template <typename T>
struct mallocator {
using value_type = T;
mallocator() = default;
template <class U>
mallocator(const mallocator<U>&) {}
T* allocate(std::size_t n) {
std::cout << "allocate(" << n << ") = ";
if (n <= std::numeric_limits<std::size_t>::max() / sizeof(T)) {
if (auto ptr = std::malloc(n * sizeof(T))) {
return static_cast<T*>(ptr);
}
}
throw std::bad_alloc();
}
void deallocate(T* ptr, std::size_t n) {
std::free(ptr);
}
};
template <typename T, typename U>
inline bool operator == (const mallocator<T>&, const mallocator<U>&) {
return true;
}
template <typename T, typename U>
inline bool operator != (const mallocator<T>& a, const mallocator<U>& b) {
return !(a == b);
}
Run Code Online (Sandbox Code Playgroud)
编辑:std::allocator_traits
尚未正确使用所有标准库.例如,上面的示例分配器std::list
在使用GCC 4.8.1编译时无法正常工作- std::list
代码抱怨缺少成员,因为它尚未更新.
类模板std::allocator<...>
没有任何虚函数.因此,提供衍生功能显然是不好的候选者.虽然一些类或类模板仍然是合理的基类,即使没有虚拟析构函数和任何其他虚函数,这些也可能只是标记类型或使用奇怪的重复模板模式.
分配器不打算像这样定制,即std::allocator<T>
不打算作为基类.如果您尝试使用它,您的逻辑可能很容易被切掉.用于轻松定制分配器的方法是依靠std::allocator_traits<A>
提供分配器选择不使用基于相对少量操作的默认实现明确提供的各种操作.
导出的主要问题std::allocator<T>
是它可能隐藏rebind
成员的问题,例如,成员被省略或拼写错误.下面是一个应该打印my_allocator::allocate()
两次但不是由于拼写错误的例子.我认为my_allocator<T>
除了错字之外,即使没有继承std::allocator<T>
,也是一个完整的分配器,即不必要的继承只会导致隐藏错误的可能性.您也可能会收到错误,例如,通过获取allocate()
或deallocate()
功能错误.
#include <memory>
#include <iostream>
template <typename T>
struct my_allocator
: std::allocator<T>
{
my_allocator() {}
template <typename U> my_allocator(my_allocator<U> const&) {}
typedef T value_type;
template <typename U> struct rebimd { typedef my_allocator<U> other; };
T* allocate(size_t n) {
std::cout << "my_allocator::allocate()\n";
return static_cast<T*>(operator new(n*sizeof(T)));
}
void deallocate(T* p, size_t) { operator delete(p); }
};
template <typename A>
void f(A a)
{
typedef std::allocator_traits<A> traits;
typedef typename traits::value_type value_type;
typedef typename traits::pointer pointer;
pointer p = traits::allocate(a, sizeof(value_type));
traits::deallocate(a, p, sizeof(value_type));
typedef typename traits::template rebind_alloc<int> other;
typedef std::allocator_traits<other> otraits;
typedef typename otraits::value_type ovalue_type;
typedef typename otraits::pointer opointer;
other o(a);
opointer op = otraits::allocate(o, sizeof(ovalue_type));
otraits::deallocate(o, op, sizeof(ovalue_type));
}
int main()
{
f(my_allocator<int>());
}
Run Code Online (Sandbox Code Playgroud)