在TC+中的练习中定位向量向量ctor的错误+;损益表 我一直在研究Bjarne Stroustrup的《C++程序设计语言》的所有练习。BR/>我已经有了一个特别的练习(E.8)。这让我很困惑。这是一个附录,讨论了标准库类中的异常安全。他举例说明了向量构造函数的一种可能实现,然后要求读者找到错误。提示表明它与析构函数有关(可能是双自由函数?)但我只是不明白他在追求什么

在TC+中的练习中定位向量向量ctor的错误+;损益表 我一直在研究Bjarne Stroustrup的《C++程序设计语言》的所有练习。BR/>我已经有了一个特别的练习(E.8)。这让我很困惑。这是一个附录,讨论了标准库类中的异常安全。他举例说明了向量构造函数的一种可能实现,然后要求读者找到错误。提示表明它与析构函数有关(可能是双自由函数?)但我只是不明白他在追求什么,c++,stl,vector,C++,Stl,Vector,据我所知,分配器可以抛出bad_alloc,终止构造函数。类似地,T上的复制构造函数可以抛出内部未初始化的_fill,这将破坏任何先前复制的元素并终止构造函数。如果其中有错误,我不清楚 该练习的措辞如下:“在vector构造函数的'messy'版本中查找错误(E.3.1),并编写程序使其崩溃。提示:首先实现向量的析构函数。 这只是一个1点练习,所以我肯定错过了一些愚蠢的明显的东西。我认为这与销毁程序的投掷无关,因为在这种情况下,所有的赌注都是无效的。也许维护“空格”和“最后”字段的不变量会有问题

据我所知,分配器可以抛出bad_alloc,终止构造函数。类似地,T上的复制构造函数可以抛出内部未初始化的_fill,这将破坏任何先前复制的元素并终止构造函数。如果其中有错误,我不清楚

该练习的措辞如下:“在vector构造函数的'messy'版本中查找错误(E.3.1),并编写程序使其崩溃。提示:首先实现向量的析构函数。

这只是一个1点练习,所以我肯定错过了一些愚蠢的明显的东西。我认为这与销毁程序的投掷无关,因为在这种情况下,所有的赌注都是无效的。也许维护“空格”和“最后”字段的不变量会有问题

我很想听听任何人的想法

以下是相关代码:

template<class T, class A = std::allocator<T> >
class vector {
private:
    T* v;
    T* space;
    T* last;
    A alloc;

    void destroy_all();
public:
    typedef size_t size_type;

    explicit vector(size_type n, const T& val = T(), const A& = A());
    vector(const vector& a);
    vector& operator=(const vector& a);
    ~vector() { destroy_all(); alloc.deallocate(v, last-v); }
    size_type size() const { return space-v; }
    size_type capacity() const { return last-v; }
    void push_back(const T&);
};

template<class T, class A>
void vector<T,A>::destroy_all() {
    for(T* p = v; p != last; p++)
        alloc.destroy(p);
}

template<class T, class A>
vector<T,A>::vector(size_type n, const T& val, const A& a) : alloc(a) {
    v = alloc.allocate(n);
    try {
        std::uninitialized_fill(v, v + n, val);
        space = last = v + n;
    }
    catch(...) {
        alloc.deallocate(v, n);
        throw;
    }
}
模板
类向量{
私人:
T*v;
T*空间;
T*last;
阿洛克;
无效销毁所有内容();
公众:
typedef size_t size_type;
显式向量(size_type n,const T&val=T(),const A&=A());
向量(常数向量&a);
向量和运算符=(常量向量和a);
~vector(){destroy_all();alloc.deallocate(v,last-v);}
size_type size()常量{return space-v;}
size_type capacity()常量{return last-v;}
无效推回(常数T&);
};
模板
void vector::destroy_all(){
对于(T*p=v;p!=last;p++)
等容破坏(p);
}
模板
向量:向量(大小类型n,常数T&val,常数A&A):alloc(A){
v=分配分配(n);
试一试{
标准:未初始化的填充(v,v+n,val);
空间=最后一个=v+n;
}
捕获(…){
分配解除分配(v,n);
投掷;
}
}

问题是
T
的构造函数可以抛出,这意味着它可以抛出:

std::uninitialized_fill(v, v + n, val);
发生这种情况时,
catch(…)
子句将释放内存,而不会正确销毁任何可能已构造的
T
对象。

可以抛出
std::bad\u alloc
。在这种情况下,您未初始化
v
,即使您从内存不足的情况下恢复,也会在销毁时进行故障隔离


另一个问题是,你的电脑。如果抛出,则释放内部存储器,而不将数据指针标记为无效。析构函数将再次释放相同的内存。

是的,在析构函数中出现异常的情况下,所有先前初始化的字段都会被正确销毁,但在这种情况下,它只是指包含三个T*字段和alloc的内存,而不是备份存储本身。必须通过alloc.deallocate()显式释放。虽然这确实发生在析构函数中,但如果析构函数因异常而终止,则不会调用它。
destroy\u all()
应该向后迭代,而不是向前迭代,但这不在构造函数中。很好,谢谢。(注意,那部分是我添加的-所以是我的错!)可能是因为构造函数没有使用
A::construct
?虽然我不确定自定义分配器是否允许与仅调用placement new有很大不同。我也认为这可能是问题所在,但根据Bjarne的说法,如果构造函数中抛出异常,未初始化的_fill()将销毁它分配的任何元素。我还验证了在我的标准库实现中也是如此。@BrettErnst Nice research。那我就及格了,把这个留着,这样你就不会再得到同样的答案了。