为什么不从std::allocater继承呢
Why not to inherit from std::allocator
我创建了自己的分配器,如下所示:
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;
};
};
但我想知道为什么我永远不应该继承std::allocator
。是因为它没有虚拟析构函数吗?我看到很多帖子说,一个人应该创建自己的,而不是继承。我理解为什么我们不应该继承std::string
和std::vector
,但继承std::allocator
有什么错?
我可以继承上面的类吗?或者我需要一个虚拟析构函数来做这件事?
为什么?
很多人会在这个线程中发布不应该从std::allocator
继承的内容,因为它没有虚拟析构函数。他们将讨论多态性以及通过指向基类的指针进行切片和删除,如标准第17.6.3.5[allocater.requirements]节所述,分配器需求不允许这些操作。除非有人证明从std::allocator
派生的类不能满足其中一个要求,否则这只是一种简单的货物崇拜心态。
也就是说,没有什么理由从C++11中的std::allocator
派生。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);
}
编辑:std::allocator_traits
的正确使用还没有完全出现在所有的标准库中。例如,上面的示例分配器在使用GCC 4.8.1编译时无法正确使用std::list
——std::list
代码抱怨缺少成员,因为它还没有更新。
类模板std::allocator<...>
没有任何虚拟函数。因此,提供派生功能显然是一个糟糕的候选者。虽然有些类或类模板仍然是合理的基类,即使没有虚拟析构函数和任何其他虚拟函数,但它们往往只是标记类型,或者使用Curioly递归模板模式。
分配器不打算像那样进行自定义,即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>());
}
我刚刚在VS2013中遇到了一个关于此的问题(但在VS2015中没有出现)。可能不是这个问题的答案,但我无论如何都会分享这个:
在boost中,有一个函数call_select_on_container_copy_construction()
测试分配器是否有成员select_on_container_copy_construction()
,并调用该函数以获得分配器的副本。当std::allocator
返回其自身的副本时,派生的myallocator
应该重写此方法以执行相同操作并返回myallocator
类型,而不是让它继承具有std::allocator
返回类型的方法。这会导致类型不匹配的编译错误。
如果myallocator
继承了std::allocator
,则它必须重写任何父方法,这些父方法可能与重写时的类型不具有相同的返回类型。
注意,据我所见,这只出现在VS2013中,所以你可能会认为这是编译器的问题,而不是代码的问题。
我使用的myallocator
是自版本3.3.0以来Eigen中的aligned_allocator
。
好吧,析构函数不是virtual
。如果您不以多态方式使用分配器,这就不是一个直接的问题。但考虑一下这种情况,其中BasicAllocator
继承自std::allocator
:
std::allocator<int>* ptr = new BasicAllocator<int>();
// ...
delete ptr;
BasicAllocator
的析构函数从未被调用,从而导致内存泄漏。
- 继承函数的重载解析
- 继承期间显示未知行为的子类
- 头文件-继承c++
- 为什么在保护模式下继承升级不起作用
- 通过继承类使用来自不同命名空间的运算符
- 子目录是否继承属性,例如add_definitions,include_directories和父Cmakelist.t
- 混合组合和继承的C++问题
- 继承:构造函数,初始化C++11中基类的类C数组成员
- 从类继承时,继承的类是否会通过父类重新定义继承的变量
- 公共与私人继承
- 如何创建从同一类继承的不同对象的向量
- 如何从另一个文件继承私有成员变量和公共函数
- 在模板基类中为继承类中的可选重写生成虚拟方法
- 带有继承的C++工厂
- 我应该避免多重实现继承吗
- C++继承更改成员
- 从具有默认值的部分指定模板类继承时发生SWIG错误,具有不带默认值的正向声明
- 关于C++中具有多重继承"this"指针的说明
- 为什么不从std::allocater继承呢
- 如何创建从std::allocater继承,它是否合理