【问题标题】:xutility read access violation on returning isstream with custom vector使用自定义向量返回 isstream 时 xutility 读取访问冲突
【发布时间】:2018-02-26 14:43:20
【问题描述】:

所以我有一个自定义的矢量类,还有一个自定义分配器:

#define _SCL_SECURE_NO_WARNINGS

#include <iostream>
#include <exception>
#include <sstream>
#include <string>
#include <iostream>

namespace mem {
    template<typename T>
    class allocator {
    public:
        //
        T * allocate(int n);                    //allocate space for n objects of type T
        void deallocate(T* p);      //deallocate n objects of type T starting at p

        void construct(T* p, const T& v);   // construct a T with the value v in p
        void destroy(T* p);                 // destroy the T in p
    };

    template<typename T> T* allocator<T>::allocate(int n)                   //allocate space for n objects of type T
    {
        T* res = (T*)malloc(sizeof(T)*n);
        if (res == nullptr)
            throw std::bad_alloc();

        return res;
    }

    template<typename T> void allocator<T>::deallocate(T* p/*, int n*/)     //deallocate n objects of type T starting at p
    {
        free(p);
    }

    template<typename T> void allocator<T>::construct(T* p, const T& v)                 // construct a T with the value v in p
    {
        new(p) T(v);
    }

    template<typename T> void allocator<T>::destroy(T* p)                   // destroy the T in p
    {
        p->~T();
    }
}

namespace vec {
    template<typename T, typename A = mem::allocator<T>>
    class vector {
        A alloc;        //use allocate to handle memory for elements
        int sz;         //the size
        T* elem;        //a pointer to the elements
        int space;      //size + free space
    public:
        using size_type = unsigned long;
        using value_type = T;
        using iterator = T * ;
        using const_iterator = const T*;

        vector() :sz{ 0 }, elem{ nullptr }, space{ 0 } {}
        explicit vector(int s) :sz{ s }, elem{ new T[s] }, space{ s }
        {
            for (int i = 0; i < sz; ++i) elem[i] = 0;           // elements are initalized
        }

        vector(const vector& arg);                              //copy constructor
        vector& operator =(const vector& a);                    //copy assignment

        vector(vector&& a);                                     //move constructor
        vector& operator=(vector&& a);                          //move assignment

        ~vector() { delete[] elem; }                            //destructor

        void reserve(int newalloc);
        void resize(int newsize, T val = T());                              //growth
        void push_back(const T& val);

        iterator begin() { return elem; }
        const_iterator begin()const { return elem; }
        iterator end() { return elem + sz; }
        const_iterator end() const { return elem + sz; }
        size_type size() { return sz; }

        iterator back() { return end() - 1; }
        const_iterator back() const { return end() - 1; }

    };

    template<typename T, typename A> vector<T, A>::vector(const vector<T, A>& a)                    //copy constructor
        :sz{ a.sz }, elem{ new T[a.sz] }
    {
        copy(a.elem, a.elem + sz, elem);
    }

    template<typename T, typename A> vector<T, A>& vector<T, A>::operator =(const vector<T, A>& a)  //copy assignment
    {
        if (this == &a) return *this;           //return if self assignment

        if (a.sz <= space) {                    //enough space no need for new allocation
            for (int i = 0; i < a.sz; ++i)      //copy elements
                alloc.construct(&elem[i], a.elem[i]);
            sz = a.sz;
            return *this;
        }

        T* p = alloc.allocate(a.sz);//allocate new size
        for (int i = 0; i < a.sz; ++i)  //copy elements
            alloc.construct(&p[i], a.elem[i]);
        for (int i = 0; i < sz; ++i)
            alloc.destroy(&elem[i]);            //destroy
        alloc.deallocate(elem);
        space = sz = a.sz;              //set new size
        elem = p;                       //set new elements
        return *this;                   //return a self reference
    }

    template<typename T, typename A> vector<T, A>::vector(vector<T, A>&& a)     //move constructor
        :sz{ a.sz }, elem{ a.elem }
    {
        a.sz = 0;                           //make a the empty vector
        a.elem = nullptr;
    }

    template<typename T, typename A> vector<T, A>& vector<T, A>::operator=(vector<T, A>&& a)            //move assignment
    {
        delete[] elem;                  //deallocate old space
        elem = a.elem;                  //copy a's elem and sz
        sz = a.sz;
        a.elem = nullptr;               //make a the empty vector
        a.sz = 0;
        return *this;
    }

    template<typename T, typename A> void vector<T, A>::reserve(int newalloc)
    {
        if (newalloc <= space) return;      //never decrease allocation
        T* p = alloc.allocate(newalloc);        //alocate new space
        for (int i = 0; i < sz; ++i)
            alloc.construct(&p[i], elem[i]);    //copy
        for (int i = 0; i < sz; ++i)
            alloc.destroy(&elem[i]);            //destroy
        alloc.deallocate(elem/*, space*/);          //deallocate old space
        elem = p;
        space = newalloc;
    }

    template<typename T, typename A> void vector<T, A>::push_back(const T& val)
    {
        if (space == 0)
            reserve(8);
        else if (space == sz)
            reserve(2 * space);
        alloc.construct(&elem[sz], val);    //add val at the end
        ++sz;
    }

    template<typename T, typename A> void vector<T, A>::resize(int newsize, T val)
    {
        reserve(newsize);
        for (int i = sz; i < newsize; ++i)
            alloc.construct(&elem[i], val);     //construct
        for (int i = newsize; i < sz; ++i)
            alloc.destroy(&elem[i]);            //destroy
        sz = newsize;
    }

    template<typename T, typename A> std::ostream& operator<<(std::ostream& os, const vector<T, A>& obj) //requires Element<T> && Allocator<A>
    {
        for (const auto& x : obj)
            os << x << ' ';
        os << '\n';
        return os;
    }

    template<typename T, typename A> std::istream& operator>>(std::istream& is, vector<T, A>& obj)
    {
        std::string line;
        std::getline(is, line);
        if (is.bad() || is.fail())
            return is;

        std::istringstream istr{ line };
        vector<T, A> tmp;

        while (true) {
            T tmp_in = T();
            istr >> tmp_in;
            if (istr.fail()) {
                istr.clear();
                std::string invalid;
                istr >> invalid;
                continue;
            }
            tmp.push_back(tmp_in);
            if (istr.eof())break;
        }

        for (const auto& x : tmp)
            obj.push_back(x);
        return is;                      //its sends me to xutility in this step
    }
}


int main()
{
    //vec::vector <int> test;       //input "1 2 3 4 5" works fine with cin >>test
    vec::vector <std::string> test;  //input "this is a test"  breaks into xutility

    while (true) {
        std::cin >> test;           //breaks into xutility
        std::cout << test << '\n';
    }
}

现在有以下问题。如果向量是向量,我可以读取向量值。所以加 1 2 3 4 5 没问题。 矢量也可以正常工作。 如果我将容器类型更改为矢量并通过 cin >> test 读入“你好,这是一个测试”;我在 xutility.h 中遇到了崩溃:

        // MEMBER FUNCTIONS FOR _Container_base12
inline void _Container_base12::_Orphan_all() _NOEXCEPT
    {   // orphan all iterators
 #if _ITERATOR_DEBUG_LEVEL == 2
    if (_Myproxy != 0)               //It points on this line
        {   // proxy allocated, drain it
        _Lockit _Lock(_LOCK_DEBUG);

        for (_Iterator_base12 **_Pnext = &_Myproxy->_Myfirstiter;
            *_Pnext != 0; *_Pnext = (*_Pnext)->_Mynextiter)
            (*_Pnext)->_Myproxy = 0;
        _Myproxy->_Myfirstiter = 0;
        }
 #endif /* _ITERATOR_DEBUG_LEVEL == 2 */
    }

Visual Studio 2017 抛出: 抛出异常:读取访问冲突。这是 0xC83FB858。

这个头文件怎么会被调用,它的含义是什么? 我真的不知道发生了什么。

我用 std::vector 替换了自定义向量,然后它也可以工作了。

编辑:我将代码最小化了一点,并为向量添加了一个命名空间。仍然是相同的行为:

vec::vector<int> test;
std::cin >> test;       //Ok: Example input 1 2 3 4 5

vec::vector<std::string> test;
std::cin >> test;       //Mem access violation in xutility: Example input "this is a test"

【问题讨论】:

  • 请阅读Why is “using namespace std” considered bad practice? 并考虑自己使用命名空间。并祈祷您包含的系统头文件中没有一个也包含系统&lt;vector&gt;头文件。
  • 这可能无法编译,因为您的全局 ::vectorstd::vector 之间的名称冲突。请提供MCVE
  • 我稍微缩短了上面的例子,并为向量添加了命名空间

标签: c++ c++11 visual-c++ vector c++14


【解决方案1】:

应用程序崩溃是因为:

  1. 代码没有始终如一地使用allocator,最终在使用new[] 分配的内存上执行free()
  2. 代码没有始终如一地维护vector::space 变量。

【讨论】:

  • 我用 alloc.allocate / alloc.deallocate 替换了所有新的 / 删除,现在看来它可以工作了。不经常使用“vector::space”是什么意思?
  • @Sandro4912 例如,vector(const vector&lt;T, A&gt;&amp; a) 构造函数使其未初始化。
  • 感谢您的建议。我根据 B.Stroustrup 的 Book PPP 做了这个向量。它分阶段开发了载体。首先没有引入空间成员。后来它被引入,但没有提到再次检查所有构造函数等以初始化它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-25
  • 2020-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多