异常安全,成本是多少 [英] Exception safe at what cost

查看:58
本文介绍了异常安全,成本是多少的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我读了Stroustrup当天的文章:
http://www.research.att.com/~bs/C++.html


使用例外编程。 InformIt.com。 2001年4月。
http://www.research。 att.com/~bs/eh_brief.pdf


其中一些想法终于开始陷入困境。我相信我看了

一段时间后同一篇文章,我觉得我还没准备好。如果我理解了正确的事情,那么他的异常安全赋值运算符的

设计似乎有点问题。我相信我已经正确地提取了所有相关代码,并对其进行了适当的评论

来描述文章中讨论的各种问题。我的问题是

在最后的评论栏中。


class Vector {// v指向一个sz整数数组

int sz;

int * v;

public:explicit Vector(int n); //创建n个int的向量

向量(const向量&);

~Vector(); //摧毁矢量

Vector& operator =(const Vector&); //赋值

int size()const;

void resize(int n); //将大小更改为n

int& operator [](int i); //下标

const int& operator [](int i)const; //下标

};


Vector :: Vector(int i)//构造函数

:sz(i), v(new int [i]){} //建立类不变量

Vector :: ~Vector(){delete [] v; }


int Vector :: size()const {return sz; } //对类表示没有影响


struct Bad_range {}; //我希望得到一个超级异常!


int& Vector :: operator [](int i)

{

if(0< = i&& i< sz)return v [i];

抛出Bad_range(); //抛出类表示没有影响

}

/ *

这是一个错误的赋值运算符实现,因为

它可能导致诸如双重删除之类的坏事情,例如,抛出
。这是因为它无法维持班级不变

表示Vector持有阵列


* /

Vector& ; Vector :: operator =(const Vector& a)

{

sz = a.sz; //获取新尺寸

delete [] v; //免费旧记忆

v = new int [n]; //获取新内存

copy(a.v,a.v + a.sz,v); //复制到新的内存

}


/ *

这是一个更好的版本,因为它保持不变

即使内存分配失败也是如此。我们可能会相信

copy()不会抛出异常,或者以其他方式失败,

所以我们不担心如果失败就失去* p。 br />
* /

Vector& Vector :: operator =(const Vector& a)

{

int * p = new int [n]; //获取新内存[或者thorw bad_alloc?]

copy(a.v,a.v + a.sz,p); //复制到新的内存


/ *在下一个语句中违反了不变量* /

sz = a.sz; //获取新尺寸

delete [] v; //免费旧记忆


/ * invariant在下一个声明中重新建立* /

v = p;

}


/ *

这是第一个表格问题的例子

的Vector :: operator =(const Vector& a)可能会出现。 Vector :: v是

由赋值运算符删除一次,然后在包含
析构函数Vector :: ~Gector() />
块。

* /

int main(){

试试

{

向量vec(10);

cout<< vec.size()<< '\\\
'; //到目前为止,那么好

Vector v2(40 * 1000000); //要求160兆字节

vec = v2; //使用另外160兆字节

}

catch(Range_error){

cerr<< 哎呀:范围错误!\ n;

}

catch(bad_alloc){

cerr<< 糟糕:内存耗尽!\ n;

}

}


/ *

现在我的观察是这样的:


如果我没有释放Vector :: v中的内存,直到在倒数第二个语句中

在赋值运算符中功能,然后我将无法分配尽可能多的内存(原始(坏)

实现。


文章早些时候Stroustrup提供了一个

析构函数的例子:~File_ptr(){if(p)fclose(p); }


如果我要使用类似的~Vector(){if(v)delete [] v; $

与赋值运算符的第一种形式相结合

函数似乎可以解决不释放

潜在可用内存的问题分配新数组。


这种方法的一个问题似乎是它违反了类不变性的

保证。这是我的

部分的正确观察吗?有没有办法在分配更多内容之前完成释放可用内存和保留类不变量的目标?

我可以想到一些方法,但它们都是似乎为分配操作员功能增加了额外费用




* /

-

STH

哈顿定律:只有一个不可侵犯的法律

KDevelop: http://www.kdevelop.org SuSE: http://www.suse.com

Mozilla: http://www.mozilla.org

I read Stroustrup''s article of the day:
http://www.research.att.com/~bs/C++.html

Programming with Exceptions. InformIt.com. April 2001.
http://www.research.att.com/~bs/eh_brief.pdf

Some of these ideas are finally beginning to sink in. I believe I looked at
the same article a while back and decided I wasn''t quite ready for it. If I
understood things correctly, there seems to be a slight problem with the
design of his exception safe assignment operator. I believe I have
correctly extracted all the relevant code, and appropriately commented it
to describe the various issues discussed in the article. My questions are
in the final comment block.

class Vector { // v points to an array of sz ints
int sz;
int* v;
public: explicit Vector(int n); // create vector of n ints
Vector(const Vector&);
~Vector(); // destroy vector
Vector& operator=(const Vector&); // assignment
int size() const;
void resize(int n); // change the size to n
int& operator[](int i); // subscripting
const int& operator[](int i) const; // subscripting
};

Vector::Vector(int i) //constructor
:sz(i) ,v(new int[i]) { } // establishes class invariant

Vector::~Vector() { delete[] v; }

int Vector::size() const { return sz; } //no effect on class representation

struct Bad_range { }; // I want an über-exception to derive from!

int& Vector::operator[](int i)
{
if (0<=i && i<sz) return v[i];
throw Bad_range(); // no effect on class representation if thrown
}
/*
This is a bad assignment operator implementation because
it can lead to bad things like double deletion when exceptions
are thrown. That''s because it fails to maintain the class invariant
that says a Vector holds an array

*/
Vector& Vector::operator=(const Vector& a)
{
sz = a.sz; // get new size
delete[] v; // free old memory
v = new int[n]; // get new memory
copy(a.v,a.v+a.sz,v); // copy to new memory
}

/*
The is a better version because it maintains the invariant
even if the memory allocation fails. We can probably trust
copy() not to throw an exception, or to otherwise fail,
so we don''t worry about losing *p if it fails.
*/
Vector& Vector::operator=(const Vector& a)
{
int* p = new int[n]; // get new memory [or thorw bad_alloc?]
copy(a.v,a.v+a.sz,p); // copy to new memory

/* invariant is violated in the next statement*/
sz = a.sz; // get new size
delete[] v; // free old memory

/* invariant is reestablished in the next statement*/
v = p;
}

/*
This is the example of where the problem with the first form
of Vector::operator=(const Vector& a) might arise. Vector::v is
deleted once by the assignment operator, and then again when the
destructor Vector::~Vector() is called upon exit of the containing
block.
*/
int main() {
try
{
Vector vec(10) ;
cout << vec.size() << ′\n′; // so far, so good
Vector v2(40*1000000) ; // ask for 160 megabytes
vec = v2; // use another 160 megabytes
}
catch(Range_error) {
cerr << "Oops: Range error!\n";
}
catch(bad_alloc) {
cerr << "Oops: memory exhausted!\n";
}
}

/*
Now my observation is this:

If I don''t free the memory in Vector::v until the penultimate statement
in the assignment operator function, then I will not be able to
allocate as much memory as I would with the original (bad)
implementation.

Earlier in the article Stroustrup provides this example of a
destructor: ~File_ptr() { if (p) fclose(p); }

If I were to use the comparable ~Vector() { if(v) delete[] v; }
in conjunction with the first form of the assignment operator
function that would seem to solve the problem of not freeing
potentially usable memory before allocating the new array.

A problem with that approach seems to be that it violates the
guarantee of class invariance. Is this a correct observation on my
part? Is there a way to accomplish both the goal of freeing available
memory prior to allocating more, and preserving the class invariant?
I can think of some approaches, but they all seem to add overhead
to the assignment operator function.

*/
--
STH
Hatton''s Law: "There is only One inviolable Law"
KDevelop: http://www.kdevelop.org SuSE: http://www.suse.com
Mozilla: http://www.mozilla.org

推荐答案

Steven T. Hatton写道:
Steven T. Hatton wrote:
我读过Stroustrup当天的文章:
http: //www.research.att.com/~bs/C++.html

使用例外编程。 InformIt.com。 2001年4月。
http://www.research。 att.com/~bs/eh_brief.pdf

其中一些想法终于开始陷入其中。我相信我一会儿看了同样的文章并决定了我还没准备好。如果我理解了正确的事情,那么他的异常安全分配操作符的设计似乎有点问题。我相信我已经正确地提取了所有相关代码,并对其进行了适当的评论,以描述文章中讨论的各种问题。我的问题在最终的评论栏中是


类Vector {// v指向一个sz整数数组
int sz;
int * v;
public:explicit Vector(int n); //创建n个int的向量
向量(const Vector&);
~Vector(); //摧毁矢量
矢量& operator =(const Vector&); //赋值
int size()const;
void resize(int n); //将大小更改为n
int& operator [](int i); //下标
const int& operator [](int i)const; //下标
};

Vector :: Vector(int i)//构造函数
:sz(i),v(new int [i]){} //建立类不变的

Vector :: ~Vector(){delete [] v; } int />
int Vector :: size()const {return sz; } //对类表示没有影响

struct Bad_range {}; //我希望得到一个超级异常!

int& Vector :: operator [](int i)
{
if(0< = i&& i< sz)return v [i];
throw Bad_range(); //抛出类表示没有影响
}

/ *
这是一个错误的赋值运算符实现,因为它可能导致像双删除这样的坏事情抛出异常
。这是因为它无法保持类不变
表示Vector持有阵列

* /
Vector& Vector :: operator =(const Vector& a)
{
sz = a.sz; //获取新大小
删除[] v; //免费旧记忆
v = new int [n]; //获取新记忆


什么'n'?不是吗?


v = new int [a.sz];


???

复制(AV,a.v + a.sz,v); //复制到新的内存
}
/ *
这是一个更好的版本,因为即使内存分配失败,它也会保持不变的状态。我们可以相信
copy()不会抛出异常,否则会失败,
所以我们不担心如果失败就失去* P.
* /
矢量& Vector :: operator =(const Vector& a)
{* / int * p = new int [n]; //获取新内存[或者thorw bad_alloc?]
copy(a.v,a.v + a.sz,p); //复制到新记忆

/ *在下一个声明中违反了不变量* /
sz = a.sz; //获取新大小
删除[] v; //免费旧记忆

/ *在下一个陈述中重新建立不变量* /
v = p;
}

/ *
这是可能出现Vector :: operator =(const Vector& a)的第一种形式的问题的例子。 Vector :: v被赋值运算符删除一次,然后在退出包含的
块时调用
析构函数Vector :: ~Gector()。
* /
int main(){
尝试
{
Vector vec(10);
cout<< vec.size()<< '\\\
'; //到目前为止,那么好
Vector v2(40 * 1000000); //要求160兆
vec = v2; //使用另一个160兆字节
}
catch(Range_error){
cerr<< 哎呀:范围错误!\ n;
}
catch(bad_alloc){
cerr<< 糟糕:内存耗尽!\ n;
}
}

/ *
现在我的观察是:

如果我没有释放Vector :: v中的内存,直到赋值运算符函数中的倒数第二个语句,那么我将无法像原始那样分配尽可能多的内存(实现。

本文前面的Stroustrup提供了这个析构函数的例子:~File_ptr(){if(p)fclose(p); }

如果我要使用类似的~Vector(){if(v)delete [] v; }


删除之前检查''v'是没用的。删除[] NULL什么也不做。

但是如果你在第一次删除后没有清除''v'(在运算符=中),

那么你就被束缚了尝试在这里再次删除它。

与第一种形式的赋值运算符
函数一起解决了在分配之前没有释放潜在可用内存的问题新阵列。


不,它不是。只有当您修复赋值运算符才能执行此操作:


Vector& Vector :: operator =(const Vector& a)

{

delete [] v; //免费旧记忆

v = 0; ///重要:不要保持指向什么的指针

sz = 0; //我们没有存储 - 指示大小

//此时对象的状态是新的,干净且一致的。

//它'不如先分配,然后再分配因为你仍然可以用这种方式丢失数据(如果新的分配失败),但至少你这个/ b $ b //双重释放没有任何问题

v = new int [a.sz]; //获得新记忆

sz = a.sz; //成功分配 - 存储新大小

副本(a.v,a.v + a.sz,v); //复制到新的内存

}


由于''new int [a.sz]''可以投掷,''v''将保留在这种情况下为0。

这种方法的一个问题似乎是它违反了类不变性的保证。这是我的
部分的正确观察吗?有没有办法在分配更多内容之前实现释放可用内存的目标,并保留类不变量?
我可以想到一些方法,但它们似乎都增加了开销
到赋值运算符函数。


真的吗?

* /
I read Stroustrup''s article of the day:
http://www.research.att.com/~bs/C++.html

Programming with Exceptions. InformIt.com. April 2001.
http://www.research.att.com/~bs/eh_brief.pdf

Some of these ideas are finally beginning to sink in. I believe I looked at
the same article a while back and decided I wasn''t quite ready for it. If I
understood things correctly, there seems to be a slight problem with the
design of his exception safe assignment operator. I believe I have
correctly extracted all the relevant code, and appropriately commented it
to describe the various issues discussed in the article. My questions are
in the final comment block.

class Vector { // v points to an array of sz ints
int sz;
int* v;
public: explicit Vector(int n); // create vector of n ints
Vector(const Vector&);
~Vector(); // destroy vector
Vector& operator=(const Vector&); // assignment
int size() const;
void resize(int n); // change the size to n
int& operator[](int i); // subscripting
const int& operator[](int i) const; // subscripting
};

Vector::Vector(int i) //constructor
:sz(i) ,v(new int[i]) { } // establishes class invariant

Vector::~Vector() { delete[] v; }

int Vector::size() const { return sz; } //no effect on class representation

struct Bad_range { }; // I want an über-exception to derive from!

int& Vector::operator[](int i)
{
if (0<=i && i<sz) return v[i];
throw Bad_range(); // no effect on class representation if thrown
}
/*
This is a bad assignment operator implementation because
it can lead to bad things like double deletion when exceptions
are thrown. That''s because it fails to maintain the class invariant
that says a Vector holds an array

*/
Vector& Vector::operator=(const Vector& a)
{
sz = a.sz; // get new size
delete[] v; // free old memory
v = new int[n]; // get new memory
What ''n''? Don''t you mean

v = new int[a.sz];

???
copy(a.v,a.v+a.sz,v); // copy to new memory
}

/*
The is a better version because it maintains the invariant
even if the memory allocation fails. We can probably trust
copy() not to throw an exception, or to otherwise fail,
so we don''t worry about losing *p if it fails.
*/
Vector& Vector::operator=(const Vector& a)
{
int* p = new int[n]; // get new memory [or thorw bad_alloc?]
copy(a.v,a.v+a.sz,p); // copy to new memory

/* invariant is violated in the next statement*/
sz = a.sz; // get new size
delete[] v; // free old memory

/* invariant is reestablished in the next statement*/
v = p;
}

/*
This is the example of where the problem with the first form
of Vector::operator=(const Vector& a) might arise. Vector::v is
deleted once by the assignment operator, and then again when the
destructor Vector::~Vector() is called upon exit of the containing
block.
*/
int main() {
try
{
Vector vec(10) ;
cout << vec.size() << ′\n′; // so far, so good
Vector v2(40*1000000) ; // ask for 160 megabytes
vec = v2; // use another 160 megabytes
}
catch(Range_error) {
cerr << "Oops: Range error!\n";
}
catch(bad_alloc) {
cerr << "Oops: memory exhausted!\n";
}
}

/*
Now my observation is this:

If I don''t free the memory in Vector::v until the penultimate statement
in the assignment operator function, then I will not be able to
allocate as much memory as I would with the original (bad)
implementation.

Earlier in the article Stroustrup provides this example of a
destructor: ~File_ptr() { if (p) fclose(p); }

If I were to use the comparable ~Vector() { if(v) delete[] v; }
Checking ''v'' before deletion is useless. delete[] NULL does nothing.
But if you don''t clear ''v'' after first deletion (in the operator=),
then you are bound to try to delete it again here.
in conjunction with the first form of the assignment operator
function that would seem to solve the problem of not freeing
potentially usable memory before allocating the new array.
No, it doesn''t. Only if you fix the assignment operator to do this:

Vector& Vector::operator=(const Vector& a)
{
delete[] v; // free old memory
v = 0; /// important: do not keep pointers that point to nothing
sz = 0; // we got no storage -- indicate that in the size
// at this point the state of the object is new, clean and consistent.
// it''s not as good as "allocate first, then assign" because you still
// can lose data this way (if new allocation fails), but at least you
// don''t have any problems with double deallocation
v = new int[a.sz]; // get new memory
sz = a.sz; // successful allocation -- store new size
copy(a.v,a.v+a.sz,v); // copy to new memory
}

Since the ''new int[a.sz]'' can throw, ''v'' will remain 0 in that case.

A problem with that approach seems to be that it violates the
guarantee of class invariance. Is this a correct observation on my
part? Is there a way to accomplish both the goal of freeing available
memory prior to allocating more, and preserving the class invariant?
I can think of some approaches, but they all seem to add overhead
to the assignment operator function.
Really?

*/




Victor



Victor

< br>

Victor Bazarov写道:
Victor Bazarov wrote:
Steven T. Hatton写道:
Steven T. Hatton wrote:
我读过Stroustrup当天的文章:
http://www.research.att.com/~bs /C++.html

使用例外编程。 InformIt.com。 2001年4月。
http://www.research。 att.com/~bs/eh_brief.pdf
[...] Vector& Vector :: operator =(const Vector& a)
{
sz = a.sz; //获取新大小
删除[] v; //免费旧记忆
v = new int [n]; //获得新记忆
什么是'n'?不是你的意思

v = new int [a.sz];

???
I read Stroustrup''s article of the day:
http://www.research.att.com/~bs/C++.html

Programming with Exceptions. InformIt.com. April 2001.
http://www.research.att.com/~bs/eh_brief.pdf [...] Vector& Vector::operator=(const Vector& a)
{
sz = a.sz; // get new size
delete[] v; // free old memory
v = new int[n]; // get new memory
What ''n''? Don''t you mean

v = new int[a.sz];

???



:D


文章中实际上存在另一个编码错误。在main()中没有打开

括号。

在删除之前检查''v'是没用的。 delete [] NULL什么都不做。
但是如果你在第一次删除后没有清除''v'(在operator =中),那么你肯定会在这里再次删除它。


我不确定''v'发生了什么事。我应该在

之前查询一下这个问题。 §5.3.5通过调用delete来通知我指针没有被取消



:D

There''s actually another coding error in the article. There''s no open
parenthesis in main().
Checking ''v'' before deletion is useless. delete[] NULL does nothing.
But if you don''t clear ''v'' after first deletion (in the operator=),
then you are bound to try to delete it again here.
I wasn''t sure what happened to ''v''. I should have looked it up before
asking the question. § 5.3.5 informs me that the pointer is not nullified
by a call to delete.

与第一种形式的赋值运算符
函数似乎解决了在分配新数组之前不释放潜在可用内存的问题。
in conjunction with the first form of the assignment operator
function that would seem to solve the problem of not freeing
potentially usable memory before allocating the new array.



不,它没有'' T。只有当您修复赋值运算符才能执行此操作:

Vector& Vector :: operator =(const Vector& a)
{
delete [] v; //免费旧记忆
v = 0; ///重要:不要保持指向什么的指针
sz = 0; //我们没有存储空间 - 表示大小
//此时对象的状态是新的,干净且一致的。
//它不如...首先分配,然后分配因为你仍然会以这种方式丢失数据(如果新的分配失败),



No, it doesn''t. Only if you fix the assignment operator to do this:

Vector& Vector::operator=(const Vector& a)
{
delete[] v; // free old memory
v = 0; /// important: do not keep pointers that point to nothing
sz = 0; // we got no storage -- indicate that in the size
// at this point the state of the object is new, clean and consistent.
// it''s not as good as "allocate first, then assign" because you still
// can lose data this way (if new allocation fails),




我没想到分配失败的后果。好点!

//但至少你
//双重释放没有任何问题
v = new int [a.sz]; //获得新记忆
sz = a.sz; //成功分配 - 存储新大小
副本(a.v,a.v + a.sz,v); //复制到新的内存


由于''new int [a.sz]''可以抛出,''v''在这种情况下将保持为0。



I didn''t think about that consequence of a failed alloc. Good point!
// but at least you
// don''t have any problems with double deallocation
v = new int[a.sz]; // get new memory
sz = a.sz; // successful allocation -- store new size
copy(a.v,a.v+a.sz,v); // copy to new memory
}

Since the ''new int[a.sz]'' can throw, ''v'' will remain 0 in that case.


这种方法的一个问题似乎是它违反了类不变性的保证。这是我的
部分的正确观察吗?有没有办法在分配更多内容之前实现释放可用内存的目标,并保留类不变量?
我可以想到一些方法,但它们似乎都增加了开销
到了赋值运算符函数。

A problem with that approach seems to be that it violates the
guarantee of class invariance. Is this a correct observation on my
part? Is there a way to accomplish both the goal of freeing available
memory prior to allocating more, and preserving the class invariant?
I can think of some approaches, but they all seem to add overhead
to the assignment operator function.



真的吗?



Really?




好​​吧,它可能不是(或似乎)很多,但你确实在函数中添加了两个赋值

语句。我真的不知道

的成本是多少。如果我在显示帧的生成期间计算10,000点质量之间的相互吸引力,那么这些赋值操作可能会加起来。

-

STH

哈顿定律:只有一个不可侵犯的法律

KDevelop: http://www.kdevelop.org SuSE: http://www.suse.com

Mozilla: http://www.mozilla.org



Well, it may not be (or seem like) a lot, but you did add two assignment
statements to the function. I really don''t know what the cost of that
might be. If I am doing something like calculating the mutual
gravitational attraction between 10,000 point masses during the generation
of a display frame, those assignment operations might add up.
--
STH
Hatton''s Law: "There is only One inviolable Law"
KDevelop: http://www.kdevelop.org SuSE: http://www.suse.com
Mozilla: http://www.mozilla.org


* Steven T. Hatton:
* Steven T. Hatton:

有没有办法在分配更多内容之前完成释放可用内存的目标,并保留类不变量?

Is there a way to accomplish both the goal of freeing available
memory prior to allocating more, and preserving the class invariant?




你可以保留班级不变但不保留内容。


-

答:因为它弄乱了人们通常阅读文字的顺序。

问:为什么这么糟糕?

A:热门帖子。

问:usenet和电子邮件中最烦人的是什么?



You can preserve the class invariant but not the contents.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?


这篇关于异常安全,成本是多少的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆