我正在编写一个std::vector
包装器,它使用自定义分配器通过.dll与PascalScript解释器通信。下面的代码工作正常,但更新和编写并破坏我的眼睛是相当繁琐的。
此解释器中的数组以&Array_Ptr[0] - sizeof(int)
的大小连续存储。
想要使用std::vector for
这些数组,我决定编写一个自定义分配器,它分配size + sizeof(int)
并让向量包装器将大小设置为ptr[0]
。因此,数据存储在&ptr[0] + sizeof(int)
。
问题是,如果我想使用矢量接口,我必须手动写出所有std::vector's
函数,因为从它继承是不好的吗?
我想出了以下代码:
template<typename T>
class PascalAllocator : public BasicAllocator<T> //BasicAllocator is equivalent to std::allocator with minor changes.
{
public:
typedef typename BasicAllocator<T>::pointer pointer;
typedef typename BasicAllocator<T>::size_type size_type;
typedef typename BasicAllocator<T>::value_type value_type;
template<typename U>
struct rebind {typedef PascalAllocator<U> other;};
pointer allocate(size_type n, const void* hint = 0)
{
std::int32_t* data_ptr = reinterpret_cast<std::int32_t*>(::operator new((n * sizeof(value_type)) + sizeof(std::int32_t)));
return reinterpret_cast<pointer>(++data_ptr);
}
void deallocate(void* ptr, size_type n)
{
if (ptr)
{
std::int32_t* data_ptr = reinterpret_cast<std::int32_t*>(ptr);
::operator delete(reinterpret_cast<T*>(--data_ptr));
}
}
};
template<typename T, typename Allocator = PascalAllocator<T>>
class PascalVector
{
private:
std::vector<T, Allocator> Data;
inline std::int32_t* size_ptr() {return reinterpret_cast<std::int32_t*>(&Data[0]) - 1;}
inline const std::int32_t* size_ptr() const {return reinterpret_cast<std::int32_t*>(&Data[0]) - 1;}
public:
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef T value_type;
typedef typename std::vector<T, Allocator>::iterator iterator;
typedef typename std::vector<T, Allocator>::const_iterator const_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
explicit PascalVector(const Allocator& alloc = Allocator()) : Data(std::forward<decltype(alloc)>(alloc)) {*size_ptr() = 0;}
explicit PascalVector(size_type size, const Allocator& alloc = Allocator()) : Data(size, std::forward<decltype(alloc)>(alloc)) {*size_ptr() = size - 1;}
explicit PascalVector(size_type size, const T &value, const Allocator& alloc = Allocator()) : Data(size, std::forward<decltype(value)>(value), std::forward<decltype(alloc)>(alloc)) {*size_ptr() = size - 1;}
template<class InputIt>
PascalVector(InputIt first, InputIt second, const Allocator &alloc = Allocator()) : Data(first, second, std::forward<decltype(alloc)>(alloc)) {*size_ptr() = Data.size() - 1;}
PascalVector(const PascalVector &other) : Data(other.Data) {}
PascalVector(const PascalVector &other, const Allocator& alloc) : Data(other.Data, std::forward<decltype(alloc)>(alloc)) {}
PascalVector(PascalVector && other) : Data(std::move(other.Data)) {}
PascalVector(PascalVector && other, const Allocator& alloc) : Data(std::move(other.Data), std::move(alloc)) {}
PascalVector(const std::initializer_list<T> &init, const Allocator& alloc = Allocator()) : Data(init, alloc) {}
inline PascalVector& operator = (PascalVector other) {Data.operator = (std::forward<decltype(other.Data)>(other.Data)); return *this;}
inline PascalVector& operator = (std::initializer_list<T> ilist) {Data.operator = (std::forward<decltype(ilist)>(ilist)); return *this;}
inline PascalVector& operator = (PascalVector && other) {Data.operator = (std::forward<decltype(other.Data)>(other.Data)); return *this;}
template<class InputIt>
inline void assign(InputIt first, InputIt second) {Data.assign(first, second);};
inline void assign(size_type count, const T& value) {Data.assign(count, std::forward<decltype(value)>(value));}
inline void assign(std::initializer_list<T> ilist) {Data.assign(std::forward<decltype(ilist)>(ilist));}
inline Allocator get_allocator() const {return Data.get_allocator();}
inline reference at(size_type pos) {return Data.at(pos);}
inline const_reference at(size_type pos) const {return Data.at(pos);}
inline reference operator[](size_type pos) {return Data[pos];}
inline const_reference operator[](size_type pos) const {return Data[pos];}
inline reference front() {return Data.front();}
inline constexpr const_reference front() const {return Data.front();}
inline reference back() {return Data.back();}
inline constexpr const_reference back() const {return Data.back();}
inline pointer data() {return Data.data();}
inline const_pointer data() const {return Data.data();}
inline iterator begin() {return Data.begin();}
inline const_iterator begin() const {return Data.begin();}
inline const_iterator cbegin() const {return Data.cbegin();}
inline iterator end() {return Data.end();}
inline const_iterator end() const {return Data.end();}
inline const_iterator cend() const {return Data.cend();}
inline reverse_iterator rbegin() {return Data.rbegin();}
inline const_reverse_iterator rbegin() const {return Data.rbegin();}
inline const_reverse_iterator crbegin() const {return Data.rbegin();}
inline reverse_iterator rend() {return Data.rend();}
inline const_reverse_iterator rend() const {return Data.rend();}
inline const_reverse_iterator crend() const {return Data.crend();}
inline bool empty() const {return Data.empty();}
inline size_type size() const {return Data.size();}
inline size_type max_size() const {return Data.max_size();}
inline void reserve(size_type new_cap) {Data.reserve(size);}
inline size_type capacity() const {return Data.capacity();}
inline void shrink_to_fit() {Data.shrink_to_fit(); *size_ptr() = Data.size() - 1;}
inline void clear() {Data.clear(); *size_ptr() = 0;}
inline iterator insert(iterator pos, const T& value) {return Data.insert(pos, std::forward<decltype(value)>(value)); *size_ptr() = Data.size() - 1;}
inline void insert(iterator pos, size_type count, const T& value) {Data.insert(pos, count, std::forward<decltype(value)>(value)); *size_ptr() = Data.size() - 1;}
template<class InputIt>
inline void insert(iterator pos, InputIt first, InputIt last) {Data.insert(pos, first, last); *size_ptr() = Data.size() - 1;}
inline void insert(iterator pos, std::initializer_list<T> ilist) {Data.insert(pos, std::forward<decltype(ilist)>(ilist)); *size_ptr() = Data.size() - 1;}
template<class... Args>
inline iterator emplace(iterator pos, Args && ... args) {iterator res = Data.emplace(pos, std::forward<Args>(args)...); *size_ptr() = Data.size() - 1; return res;}
template<class... Args>
inline void emplace_back(Args && ... args) {Data.emplace_back(std::forward<Args>(args)...); *size_ptr() = Data.size() - 1;}
inline iterator erase(iterator pos) {iterator res = Data.erase(pos); *size_ptr() = Data.size(); return res;}
inline iterator erase(iterator first, iterator last) {iterator res = Data.erase(first, last); *size_ptr() = Data.size() - 1; return res;}
inline void push_back(const T& value) {Data.push_back(std::forward<decltype(value)>(value)); *size_ptr() = Data.size() - 1;}
inline void push_back(T && value) {Data.push_back(std::forward<T>(value)); *size_ptr() = Data.size() - 1;}
inline void pop_back() {Data.pop_back(); *size_ptr() = Data.size() - 1;}
inline void resize(size_type count, T value = T()) {Data.resize(count, std::forward<decltype(value)>(value)); *size_ptr() = count - 1;}
inline void swap(PascalVector& other) {Data.swap(other.Data);}
};
现在PascalAllocator
很好。令我讨厌的是PascalVector
界面。
有任何更简单的方法吗?
修改
根据我得到的一些回复,我尝试按照以下方式实现自己的向量:
template<typename T, typename Allocator = PascalAllocator<T>>
class PSArray : private Allocator
{
private:
typename Allocator::pointer first;
typename Allocator::pointer last;
typename Allocator::size_type _size;
typename Allocator::pointer allocmem(typename Allocator::size_type n, const T& value);
void deallocmem();
public:
typedef T value_type;
typedef typename Allocator::pointer pointer;
typedef typename Allocator::const_pointer const_pointer;
typedef typename Allocator::reference reference;
typedef typename Allocator::const_reference const_reference;
typedef typename Allocator::size_type size_type;
typedef typename Allocator::difference_type difference_type;
typedef typename Allocator::pointer iterator;
typedef typename Allocator::const_pointer const_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef Allocator allocator_type;
allocator_type get_allocator() const {return static_cast<const Allocator&>(*this);}
iterator begin() {return first;}
iterator end() {return last;}
const_iterator begin() const {return first;}
const_iterator end() const {return last;}
size_type size() const {return _size;}
PSArray(size_type n = 0, const T& value = T(), const Allocator& alloc = Allocator());
PSArray(const PSArray &other);
PSArray(PSArray&& other);
~PSArray();
PSArray& operator = (const PSArray &other);
inline reference operator[](size_type pos) {return first[pos];}
inline const_reference operator[](size_type pos) const {return first[pos];}
};
template <class T, class Allocator>
PSArray<T, Allocator>::PSArray(size_type n, const T& value, const Allocator& alloc) : Allocator(alloc), first(0), last(0), _size(0)
{
this->first = this->allocmem(n, value);
this->last = &first[0] + n + 1;
reinterpret_cast<std::int32_t*>(first)[-1] = n - 1; //*(reinterpret_cast<std::int32_t*>(&first[0]) - 1) = n - 1;
_size = n;
}
template <class T, class Allocator>
PSArray<T, Allocator>::PSArray(const PSArray &other) : Allocator(other.get_allocator()), first(0), last(0), _size(other._size)
{
this->first = Allocator::allocate(other._size);
this->last = &first[0] + _size + 1;
memcpy(&first[0], &other.first[0], other._size * sizeof(T));
reinterpret_cast<std::int32_t*>(first)[-1] = _size - 1; //*(reinterpret_cast<std::int32_t*>(&first[0]) - 1) = _size - 1;
}
template <class T, class Allocator>
PSArray<T, Allocator>::PSArray(PSArray&& other) : first(other.first), last(other.last), _size(other._size)
{
other.first = nullptr;
other.last = nullptr;
other._size = 0;
}
template <class T, class Allocator>
PSArray<T, Allocator>::~PSArray()
{
this->deallocmem();
}
template <class T, class Allocator>
PSArray<T, Allocator>& PSArray<T, Allocator>::operator = (const PSArray &other)
{
_size = other._size;
this->first = Allocator::allocate(other._size);
this->last = &first[0] + _size + 1;
memcpy(&first[0], &other.first[0], other._size * sizeof(T));
reinterpret_cast<std::int32_t*>(first)[-1] = _size - 1;
return *this;
}
template <class T, class Allocator>
typename Allocator::pointer PSArray<T, Allocator>::allocmem(typename Allocator::size_type n, const T& value)
{
if (n != 0)
{
size_type i = 0;
typename Allocator::pointer res = Allocator::allocate(n);
try
{
for (i = 0; i < n; ++i)
{
Allocator::construct(res + i, value);
}
}
catch(...)
{
for(size_type j = 0; j < i; ++j)
{
Allocator::destroy(res + j);
}
Allocator::deallocate(res, n);
throw;
}
return res;
}
return nullptr;
}
template <class T, class Allocator>
void PSArray<T, Allocator>::deallocmem()
{
if (first != last)
{
for (iterator i = first; i < last; ++i)
{
Allocator::destroy(i);
}
Allocator::deallocate(first, last - first);
}
}
到目前为止工作得非常好。尽管如此,以前的代码还有很多工作要做。
答案 0 :(得分:5)
让PascalVector
看起来像std::vector
看起来没有比实现std::vector
的整个界面更简单的方法了。
std::vector
,因为std::vector
没有虚拟析构函数,这意味着删除指向std::vector
的指针不会调用PascalVector
的析构函数即使指针指向PascalVector
。std::vector
,因为std::vector
中缺少虚函数,您将针对实现进行编程,而不是接口。std::vector
的{{1}}的typedef是不够的,因为PascalAllocator
没有被告知向量大小的变化(因此无法将大小写入正确位置)。 BTW:类中定义的函数自动被视为PascalAllocator
。在这些情况下,inline
关键字是多余的。