Correct way to initialize dynamic Array in C++
我目前正在研究一个C++项目,其中经常出现动态数组。我想知道,使用新的操作符初始化动态数组的正确方法是什么?我的一位同事告诉我,在构造函数中不应该使用new,因为构造函数是一个不应该容易出错或完全不应该失败的构造。现在让我们考虑下面的例子:我们有两个类,一个或多或少复杂的类状态和一个类状态容器,应该进行自我解释。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 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.)可以在构造函数中调用new,还是最好为状态数组创建一个额外的init()-方法,为什么?
2.)检查新产品是否成功的最佳方法是:
1 | if (!ptr) std::cerr <<"new failed." |
或
1 | try { /*new*/ } catch (std::bad_alloc) { /*handling*/ } |
3)好的,它有三个问题;o)在引擎盖下面,新的做了一些
1 | ptr = (Struct *)malloc(N*sizeof(Struct)); |
然后调用构造函数,对吗?
你应该让
首先,从构造函数中抛出异常是表示问题的唯一可靠方法——如果没有异常,则表示对象已完全构造。因此,单独捕获
那么,您可以做些什么来"处理"它,使其他代码能够感知并做出适当的反应呢?
使用例外权-让它们传播到可以合理处理它们的站点。
构造函数的全部目的是构造一个对象。包括初始化。当构造函数完成执行时,对象应该准备好使用。这意味着构造函数应该执行任何必要的初始化。
你朋友的建议导致了不可维护和容易出错的代码,违背了每一个优秀的C++实践。他错了。
至于动态数组,请使用
1 | std::vector<int>(10, 20) |
将创建一个10个整数的向量,所有整数都初始化为值20。
简短回答:
不,你朋友错了。构造函数是您进行分配+初始化的地方。我们甚至有一个术语叫做"资源获取就是初始化"(raii)。类在构造函数中作为初始化的一部分获取资源,类在析构函数中释放那些获得的资源。
长答案:
在构造函数中考虑以下代码:
1 2 | member1 = new whatever1[n]; member2 = new whatever2[m]; |
现在,假设上面的第二个分配是抛出异常,因为WITEVER 2的构造失败,并抛出异常,或者分配失败,并抛出STD::BADYOLL。结果是,为1版分配的内存将被泄漏。
因此,最好使用容器类,例如:STD::
1 2 | MyClass::MyClass(std::size_t n, std::size_t m) : member1(n), member2(m) {} // where member1 and member2 are of type std::vector |
当使用STD::vector,如果第二次分配失败,将调用先前的STD::vector的析构函数,从而使资源被适当地释放。这可能就是你的朋友说你不应该使用new的意思(你应该使用一个容器类,而不是),在这种情况下,他基本上是正确的……虽然有一些智能指针类,比如boost::shared_ptr,它们为您提供了相同的安全保证,并且您仍然需要在哪里使用新的,但是他仍然不太正确。
请注意,如果您只有一个要分配的对象/数组,那么这不是一个问题…这就是代码中的情况…您不必担心由于某些其他分配失败而导致的泄漏。另外,我应该补充一下,new要么成功,要么抛出一个异常;它不会返回空值,因此检查是无意义的。唯一的STD:您必须执行几个分配(您不允许使用STD::vector),在这种情况下,您可以释放处理程序中的其他资源,但是您会重新抛出异常,因为应该让它传播。
不是一个完整的答案,只是我的2分:
1:我会在构造函数中使用new,虽然对于动态数组,stl是一种方法。
2:new的正常错误处理是引发异常,因此检查返回的指针是无用的。
3:别忘了新来的接线员,让故事更有趣一点。
如果要查找返回类型,即如果函数必须返回状态,则使用单独的函数(init())分配内存。
如果要检查是否通过检查所有成员函数中的空条件来分配内存,请在构造函数本身中分配内存。
异常处理(即Try…Catch)是更好的选择。
除了调用构造函数,"this"指针也被初始化。
你的朋友是对的。但是,通常的做法是在构造函数中保留内存,在析构函数中释放内存。
构造函数中可能失败的方法的问题如下:构造函数没有传统的方法将问题通知调用方。调用方希望获得一个以任何方式都未损坏的对象实例。
这个问题的解决方案是抛出/传播一个异常。异常将始终通过,并且可以由调用方处理。