2010-04-30 81 views
7

我目前正在开发一个经常出现动态数组的C++项目。 我想知道,什么可能是正确的方式来初始化动态数组使用新的运算符?我的一位同事告诉我,在构造函数中使用new是不允许的,因为构造函数是一个不应该容易出错或者不应该失败的构造函数。现在我们来考虑下面的例子:我们有两个类,一个或多或少复杂的类State和一个StateContainer类,应该自我解释。在C++中初始化动态数组的正确方法

class State { 
private: 
unsigned smth; 
public: 
State(); 
State(unsigned s); 
}; 

class StateContainer { 
private: 
unsigned long nStates; 
State *states; 
public: 
StateContainer(); 
StateContainer(unsigned long n); 
virtual ~StateContainer(); 
}; 

StateContainer::StateContainer() { 
nStates = SOME_DEFINE_N_STATES; 
states = new State[nStates]; 
if (!states) { 
    // Error handling 
} 
} 

StateContainer::StateContainer(unsigned long n) { 
nStates = n; 
try { 
    states = new State[nStates] 
} catch (std::bad_alloc &e) { 
    // Error handling 
} 
} 

StateContainer::~StateContainer() { 
if (states) { 
    delete[] states; 
    states = 0; 
} 
} 

现在实际上,我有两个问题:

1)它是确定,在构造函数中调用新的,或者是更好地创建一个额外的init() - 为状态 - 法数组和为什么?

2)请告诉我要检查的最好的方式,如果新的成功:

if (!ptr) std::cerr << "new failed." 

try { /*new*/ } catch (std::bad_alloc) { /*handling*/ } 

3)确定的三个问题; O)引擎盖下,新做某种

ptr = (Struct *)malloc(N*sizeof(Struct)); 

然后调用构造函数吧?

+8

我假设你不能使用'VECTOR'? – GManNickG 2010-04-30 07:51:50

+0

你是同事是错的。在构造函数中使用'new'完全可以接受。 – 2010-04-30 15:19:54

回答

7

你应该让std::bad_alloc传播 - 无论如何,这可能没有什么合理的。

首先,从构造函数is the only reliable way to signal a problem中抛出一个异常 - 如果没有异常,则表示该对象是完全构造的。因此单独采用std::bad_alloc无助于解决其他可能的例外情况。

那么你能做些什么来“处理”它的方式,使其他代码知道并可以做出适当的反应?

使用例外权利 - let them propagate to the site where they can be reasonably handled

0

你的朋友有点不对。但是,在构造函数中执行内存预留以及在Destructor中释放内存是很常见的做法。

构造函数中可能失败的方法的问题如下所示:构造函数没有传统方法来通知调用者该问题。调用者希望得到一个没有任何损坏的对象实例。

此问题的解决方案是抛出/传播异常。该异常将始终通过,并可以由调用者处理。

+0

你的答案是矛盾的;你说没有传统的方式通知用户一个错误,然后继续描述一个。 – 2010-04-30 08:27:09

+1

他的朋友是错误的,首都W.故事结束。 – jalf 2010-04-30 08:34:06

+0

例外如此不受欢迎,大多数C++时,才必须使用它们。在他们的“传统”的思想有例外没有地方,这就是为什么他们看不到他们的方式来通知用户。 – ypnos 2010-05-01 12:32:23

1

不是一个完整的答案,只是我的2美分:

1:我会在构造函数中使用新的,虽然对于动态数组,STL是要走的路。

2:new的正常错误处理是引发异常,因此检查返回的指针是没有用的。

3:不要忘记新运营商,让故事更有趣一点。

5

构造的整个目的是构建的对象。这包括初始化。当构造函数完成执行时,对象应该可以使用了。这意味着构造函数应该执行任何必要的初始化。

你的朋友建议导致难以维护,而且容易出错代码,违背每一个良好的C++的做法。他错了。

至于你的动态数组,使用std::vector代替。并初始化它,只需一个参数传递给构造函数:

std::vector<int>(10, 20) 

将创建10个整数的向量,所有的人都初始化为20

+0

“包含”是什么意思?对我而言,*构造*和*初始化*是同义词。 – fredoverflow 2010-04-30 11:16:18

0
  1. 如果您正在寻找出返回类型,即如果函数必须返回状态,则使用单独的函数(init())分配内存。

    如果检查要检查内存是否得到了由所有成员函数检查NULL条件,那么在构造函数本身分配内存分配。

  2. 异常处理(即try ... catch)是一个更好的选择。

  3. 除了调用构造函数外,“this”指针也被初始化。

1

简短的回答
不,你的朋友是错误的。构造函数是你做分配+初始化的地方。我们甚至有一个名为“资源获取是初始化”(RAII)的术语......类在构造函数中获取资源作为其初始化的一部分,类将这些获取的资源释放到它们的析构函数中。

龙回答

考虑下面的代码在构造函数中:

member1 = new whatever1[n]; 
member2 = new whatever2[m]; 

现在,假设在上述第二分配是抛出一个异常,或者是因为建设whatever2失败并抛出一个异常,或分配失败并抛出std :: bad_alloc。结果是你为any1分配的内存将被泄露。

由于这个原因,最好是使用一个容器类,如std ::向量:

MyClass::MyClass(std::size_t n, std::size_t m) : member1(n), member2(m) {} 
// where member1 and member2 are of type std::vector 

当使用类型的std ::矢量,如果第二分配失败,先前的std的析构函数:: vector将被调用,导致资源被适当释放。这可能是你的朋友说他不应该使用(你应该使用一个容器类),在这种情况下,他会是最正确的......虽然有智能指针类,如boost :: shared_ptr为您提供这些相同的安全保证,并且您仍然需要使用新的,因此他仍然不太正确。

请注意,如果您只有一个对象/数组,你是分配,那么这是不是一个问题......这是事物的方式是在你的代码......你不必担心由于泄漏到其他一些分配失败。另外,我要补充的是将成功或抛出一个异常;它不会返回NULL,所以检查是毫无意义的。在这里你应该抓住的std :: bad_alloc的唯一实例是在你已经被迫做一些分配(你是从使用std :: vector的禁止),在这种情况下,你在处理程序解除分配其他资源的情况下,但随后你重新抛出异常,因为你应该让它传播。