堆叠展开
Stack unwinding
有人告诉我,将所有指针放在catch块中是糟糕的OO编程。清理发生在catch块中。它是如何违反OO设计的每一条规则的?
这是一个示例代码:
#include <iostream>
#include <string>
using namespace std;
class Error
{
friend int main();
public:
Error(int* p, string m) : arr(p), msg(m) { }
private:
int* arr;
string msg;
};
void initialize();
int main()
{
try {
initialize();
}
catch(Error& err) {
cout<<endl<< "Error! "<< err.msg <<endl<<endl;
delete [] err.arr;
}
return 0;
}
void initialize()
{
int size;
cout<<"Enter the number of elements: ";
cin >> size;
int* myArray = new int[size];
cout<<"Enter the elements: " <<endl;
for (int i=0; i<size; ++i)
cin >> myArray[i];
if (!cin.good())
throw Error(myArray, (string)"bad input!");
cout<<endl<<"You entered:"<<endl;
for (int i=0; i<size; ++i)
cout << myArray[i] << " ";
cout<<endl;
delete [] myArray;
}
请忽略这一行。我只是想把这个问题公布出来。
为了处理资源,C过去专注于管理执行路径。程序员必须确保对于每一条可能的路径,都释放了资源。
所以以这样的代码结束是正常的:请注意,大部分代码都是用来处理错误的。
HRESULT
CreateNotifyIcon(NotifyIcon** ppResult)
{
NotifyIcon* icon = 0;
Icon* inner = 0;
const wchar_t * tmp1 = 0;
HRESULT hr = S_OK;
if ( SUCCEEDED(hr) ) {
icon = new (nothrow) NotifyIcon();
if ( !icon ) hr = E_OUTOFMEM;
}
if ( SUCCEEDED(hr) )
hr = icon->set_text("Blah blah blah");
if ( SUCCEEDED(hr) ) {
inner = new (nothrow) Icon(...);
if ( !inner )
hr = E_OUTOFMEM;
else {
Info info;
hr = GetInfo( &info );
if ( SUCCEEDED(hr) )
hr = icon->set_icon(inner, info);
if ( SUCCEEDED(hr) )
inner = NULL;
}
}
if ( SUCCEEDED(hr) )
hr = icon->set_visible(true);
if ( SUCCEEDED(hr) ) {
*ppResult = icon;
icon = NULL;
} else {
*ppResult = NULL;
}
cleanup:
if ( inner ) delete inner;
if ( icon ) delete icon;
return hr;
}
在C++中,这不是正确的方法,因为您有例外。例如:
String EvaluateSalaryAndReturnName( Employee e )
{
if( e.Title() == "CEO" || e.Salary() > 100000 )
{
cout << e.First() << " " << e.Last()
<< " is overpaid" << endl;
}
return e.First() + " " + e.Last();
}
该代码片段中有23不同的执行路径。
因此,C++选择将重点放在资源上。每个函数(应该)处理有限数量的资源。粗略地说,你在每个资源上都设置了一个监督程序,以确保它们被正确释放。这个看门狗是RAII。事实上,无论执行路径是什么,您都可以100%确定将调用堆栈上分配的所有对象的析构函数。这样,通过将资源放入RAII对象(STL容器,std::unique_ptr,…),您可以处理异常,而不会出现任何资源泄漏的问题。
看看区别:糟糕的方式
void function(int n){
int* p = 0;
int* c = 0;
try{
p = new int[n];
c = new int[n*2];
}
catch(std::exception const& e){
delete[] c;
delete[] p;
throw;
}
delete[] c;
delete[] p;
}
int main(){
try{
function(1000);
} catch (std::exception const& e){
std::cerr<<e.what()<<std::endl;
}
}
好方法
void function(int n){
std::unique_ptr<int[]> p(new int[n]); //or std::vector
std::unique_ptr<int[]> c(new int[n*2]);
}
int main(){
try{
function(1000);
} catch (std::exception const& e){
std::cerr<<e.what()<<std::endl;
}
}
问题是,必须以所有可能的方式删除数组才能离开函数。如果你只有一两种方法,这可能很容易,但会混淆更多。即使在你的代码中,你也有一个已经在函数之外的删除,这让你很难找到。
使用智能指针来针对该问题。当内容超出范围时,它们会解除分配。这样你就不必为阵列的破坏而烦恼。函数一完成,数组就会被销毁。
以下是一些智能指针的文档:unique_ptrshared_ptr
C++标准n3337§15.2/3说:
为构造的自动对象调用析构函数的过程从try块到throw表达式的路径称为"堆栈"展开。(…)"
代码的问题在于,指向数组的指针是在try块内分配的,所以当控件到达catch块时,它就不再活动了。你不能做
要更正此问题,您应该在尝试块之前声明一个指针:
int* myArray;
try{
function(1000); // allocate an array and point myArray to it
} catch (std::exception const& e){
delete [] myArray; // OK, myArray pointer is valid here
}
这将删除对象并将内存返回系统。在CCD_ 1中采用了这种方法。但你有更好的可能性。为了摆脱释放内存的负担,您可以考虑使用智能指针或句柄来访问数组(一个围绕资源的类):将每个资源表示为一个类是RAII方法的基础。
try{
MyArray myArray(1000); // allocate an array in constructor
} catch (std::exception const& e){
// destructor for myArray has deleted ints & returned memory already
}
- 没有找到相关文章