我正在编写一个
在这个解释器中,数组是连续存储的,其大小存储在
想要使用
问题是如果我想使用向量接口,我必须手动编写所有
我想出了以下代码:
现在的
有什么更简单的方法吗?
编辑: 根据我收到的一些回复,我尝试按以下方式实现自己的向量:
std::vector
包装器,它使用自定义分配器通过.dll与PascalScript解释器通信。下面的代码可以正常工作,但更新和编写都很繁琐,而且会让我感到眼花缭乱。在这个解释器中,数组是连续存储的,其大小存储在
&Array_Ptr [0] - sizeof(int)
处。想要使用
std::vector
来处理这些数组,我决定编写一个自定义分配器,它分配size + sizeof(int)
并将向量包装器放置在ptr [0]
处。因此,数据存储在&ptr [0] + sizeof(int)
处。问题是如果我想使用向量接口,我必须手动编写所有
std::vector
函数,因为从中继承将是不好的?我想出了以下代码:
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);
}
}
到目前为止,它的表现相当不错。但与之前的代码相比,需要更多的工作。
std::vector
呢?例如:template<typename T> using PascalVector = std::vector<T, PascalAllocator<T>>;
- Some programmer dudevector<T, Allocator>
私有继承来减少样板代码,而不是使用数据成员。你仍然需要一堆using
语句,每个不同的名称都需要一个,但比所有那些传递函数要少。 - Steve Jessopvector
公开继承有点糟糕,但私有继承就没问题了。从未设计用于公开继承的类进行公开继承有两个问题:(1)vector
没有虚析构函数,因此用户需要知道不要这样做:vector<int, PascalAllocator<int>> *ptr = new PascalVector<int>(); delete ptr;
。实际上,这并不是很难安排,只是意味着你的类有一个小小的陷阱。(2)没有精确定义vector
的哪些函数会相互调用,而且在派生类中重载的任何内容都不能被基类调用。 - Steve Jessopusing
通过函数传递,你都需要考虑基类中的实现是否足够。您可能错误地假定,例如,clear
调用erase(begin(), end())
并且您已经重载了erase
,所以没问题。但那行不通(即使clear
调用了erase
,它也无法调用您重载的erase
)。您还需要重载clear
,而事实上您已经这样做了。所以你必须知道自己在做什么 :-) - Steve Jessopreinterpret_cast
将void*
转换并不必要。使用static_cast
就足够了(而且可以更好地表达意图)。 - dyp