关于new operator:在C ++中初始化动态数组的正确方法

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));

然后调用构造函数,对吗?


你应该让std::bad_alloc繁殖——你无论如何都不可能做任何合理的事情。

首先,从构造函数中抛出异常是表示问题的唯一可靠方法——如果没有异常,则表示对象已完全构造。因此,单独捕获std::bad_alloc对其他可能的异常情况没有帮助。

那么,您可以做些什么来"处理"它,使其他代码能够感知并做出适当的反应呢?

使用例外权-让它们传播到可以合理处理它们的站点。


构造函数的全部目的是构造一个对象。包括初始化。当构造函数完成执行时,对象应该准备好使用。这意味着构造函数应该执行任何必要的初始化。

你朋友的建议导致了不可维护和容易出错的代码,违背了每一个优秀的C++实践。他错了。

至于动态数组,请使用std::vector。要初始化它,只需向构造函数传递一个参数:

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"指针也被初始化。


  • 你的朋友是对的。但是,通常的做法是在构造函数中保留内存,在析构函数中释放内存。

    构造函数中可能失败的方法的问题如下:构造函数没有传统的方法将问题通知调用方。调用方希望获得一个以任何方式都未损坏的对象实例。

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