我被告知将所有指针放在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;
}
请忽略此行。我只是试着发布这个问题。
答案 0 :(得分:2)
问题是,你必须以所有可能的方式删除数组以离开该函数。如果您只有一种或两种方式,这可能很容易,但更容易混淆。即使在您的代码中,您已经在函数外部进行了一次删除,但却很难找到它。
使用智能指针来定位该问题。当他们超出范围时,他们会释放内容。这样你就不必为数组的破坏而烦恼了。一旦完成该功能,阵列将被销毁。
以下是智能指针的一些文档: unique_ptr shared_ptr
答案 1 :(得分:2)
为了处理资源,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 different execution个路径。
所以C ++选择专注于资源。每个函数(应该)处理有限数量的资源。粗略地说,您在每个资源上放置一个监视程序,以确保它们被正确释放/释放。这只看门狗是 RAII 。实际上,无论执行路径是什么,您都可以100%确定将调用堆栈上分配的所有对象的析构函数。这样,通过将您的资源放入RAII对象(STL容器,std :: unique_ptr,...),您可以处理异常,而不会出现任何泄漏资源问题。
看看差异: BAD WAY
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;
}
}
答案 2 :(得分:0)
C ++标准版n3337 § 15.2 / 3说:
为构造的自动对象调用析构函数的过程 在从try块到throw-expression的路径上称为“堆栈” 平仓。 (...)”
你的代码的问题是指向数组的指针是在try块中分配的,所以当控制到达catch块时它不再存在。你做不到
要纠正这个问题,你应该在try block:
之前声明一个指针 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
}
这将删除对象并将内存返回给系统。例如,在std::uninitialized_fill
中采用这种方法。但是你有更好的可能性。为了摆脱释放内存的负担,您可以考虑将智能指针或句柄用于数组(包装资源的类):将每个资源表示为class是名为RAII的方法的基础。
try{
MyArray myArray(1000); // allocate an array in constructor
} catch (std::exception const& e){
// destructor for myArray has deleted ints & returned memory already
}