6

我正在使用一个低级 API,它接受一个char*数字值来分别表示一个字符串及其长度。我的代码通过std::basic_string适当的翻译使用并调用这些方法。不幸的是,这些方法中的许多方法都接受不同大小的字符串长度(即 max( unsigned char)、max( short) 等),我一直在编写代码以确保我的字符串实例不超过低级 API。

默认情况下,std::basic_string实例的最大长度受size_t(max( unsigned int) 或 max( __int64))的最大值限制。有没有办法操纵实现的特征和分配器实现,std::basic_string以便我可以指定我自己的类型来代替size_t?通过这样做,我希望在实现中利用任何现有的边界检查,std::basic_string因此在执行翻译时我不必这样做。

我的初步调查表明,如果不编写我自己的字符串类,这是不可能的,但我希望我忽略了一些东西:)

4

3 回答 3

5

您可以传递一个自定义分配器,std::basic_string该分配器具有您想要的最大大小。这应该足够了。也许是这样的:

template <class T>
class my_allocator {
public:
    typedef T              value_type;

    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;

    pointer address(reference r) const             { return &r; }
    const_pointer address(const_reference r) const { return &r; }

    my_allocator() throw() {}

    template <class U>
    my_allocator(const my_allocator<U>&) throw() {}

    ~my_allocator() throw() {}

    pointer allocate(size_type n, void * = 0) {
        // fail if we try to allocate too much
        if((n * sizeof(T))> max_size()) { throw std::bad_alloc(); }
        return static_cast<T *>(::operator new(n * sizeof(T)));
    }

    void deallocate(pointer p, size_type) {
        return ::operator delete(p);
    }

    void construct(pointer p, const T& val) { new(p) T(val); }
    void destroy(pointer p)                 { p->~T(); }

    // max out at about 64k
    size_type max_size() const throw() { return 0xffff; }

    template <class U>
    struct rebind { typedef my_allocator<U> other; };

    template <class U>
    my_allocator& operator=(const my_allocator<U> &rhs) {
        (void)rhs;
        return *this;
    }
};

然后你可能可以这样做:

typedef std::basic_string<char, std::char_traits<char>, my_allocator<char> > limited_string;

编辑:我刚刚做了一个测试,以确保它按预期工作。以下代码对其进行测试。

int main() {
    limited_string s;
    s = "AAAA";
    s += s;
    s += s;
    s += s;
    s += s;
    s += s;
    s += s;
    s += s; // 512 chars...
    s += s;
    s += s;
    s += s;
    s += s;
    s += s;
    s += s; // 32768 chars...
    s += s; // this will throw std::bad_alloc

    std::cout << s.max_size() << std::endl;
    std::cout << s.size() << std::endl;
}

最后一个s += s会将它放在顶部并导致std::bad_alloc异常,(因为我的限制只是短于 64k)。不幸的是 gcc 的std::basic_string::max_size()实现并不基于你使用的分配器,所以它仍然声称能够分配更多。(我不确定这是否是一个错误......)。

但这肯定会让您以简单的方式对字符串的大小施加硬性限制。您甚至可以将最大大小设为模板参数,这样您只需为分配器编写一次代码。

于 2009-10-19T23:33:29.450 回答
4

我同意 Evan Teran 关于他的解决方案。这只是对他的解决方案的修改:

template <typename Type, typename std::allocator<Type>::size_type maxSize>
struct myalloc : std::allocator<Type>
{
    // hide std::allocator[ max_size() & allocate(...) ]

    std::allocator<Type>::size_type max_size() const throw()
    {
        return maxSize;
    }
    std::allocator<Type>::pointer allocate
        (std::allocator<Type>::size_type n, void * = 0)
    {
        // fail if we try to allocate too much
        if((n * sizeof(Type))> max_size()) { throw std::bad_alloc(); }
        return static_cast<Type *>(::operator new(n * sizeof(Type)));
    }
};

请注意,您根本不应该对myalloc. 所以这是灾难性的:

// std::allocator doesn't have a virtual destructor
std::allocator<char>* alloc = new myalloc<char>;

您只需将它用作单独的类型,在以下情况下是安全的:

myalloc<char, 1024> alloc; // max size == 1024
于 2009-10-19T23:49:41.383 回答
0

你不能创建一个以 std::string 作为父类并覆盖 c_str() 的类吗?还是定义自己的 c_str16()、c_str32() 等并在那里实现翻译?

于 2009-10-19T23:18:42.070 回答