简体   繁体   English

在C ++中初始化动态数组的正确方法

[英]Correct way to initialize dynamic Array in C++

I'm currently working on a C++ project, where dynamic arrays often appear. 我目前正在开发一个C ++项目,其中经常出现动态数组。 I was wondering, what could be the correct way to initialize a dynamic array using the new-operator? 我想知道,使用new-operator初始化动态数组的正确方法是什么? A colleague of mine told me that it's a no-no to use new within the constructor, since a constructor is a construct that shouldn't be prone to errors or shouldn't fail at all, respectively. 我的一位同事告诉我,在构造函数中使用new是一个禁忌,因为构造函数是一个不应该容易出错或者根本不应该失败的构造。 Now let's consider the following example: We have two classes, a more or less complex class State and a class StateContainer, which should be self-explained. 现在让我们考虑以下示例:我们有两个类,一个或多或少复杂的类State和一个StateContainer类,应该自我解释。

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

Now actually, I have two questions: 实际上,我有两个问题:

1.) Is it ok, to call new within a constructor, or is it better to create an extra init()-Method for the State-Array and why? 1.)是否可以在构造函数中调用new,或者更好的是创建一个额外的init() - State-Array的方法以及为什么?

2.) Whats the best way to check if new succeeded: 2.)什么是检查新成功的最佳方法:

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

or 要么

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

3.) Ok its three questions ;o) Under the hood, new does some sort of 3.)好了它的三个问题; o)在引擎盖下,新的确有某些问题

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

And then call the constructor, right? 然后调用构造函数,对吧?

You should let the std::bad_alloc propagate - there's likely nothing reasonable you could do anyway. 你应该让std::bad_alloc传播 - 无论如何你可能无法做任何合理的事情。

First of all, throwing an exception from the constructor is the only reliable way to signal a problem - if there's no exception it means the object is completely constructed. 首先,从构造函数中抛出异常是发出问题信号的唯一可靠方法 - 如果没有异常则意味着对象是完全构造的。 So catching std::bad_alloc alone will not help against other possible exceptions. 因此,单独捕获std::bad_alloc无助于防止其他可能的异常。

Then what can you do to "handle" it in such a way that the other code is aware and can react appropriately? 那么你可以做些什么来“处理”它,以便其他代码知道并且可以做出适当的反应?

Use exceptions right - let them propagate to the site where they can be reasonably handled . 正确使用例外 - 让它们传播到可以合理处理的网站

The entire purpose of a constructor is to construct an object. 构造函数的整个目的是构造一个对象。 That includes initialization. 这包括初始化。 When the constructor finishes executing, the object should be ready to use. 当构造函数完成执行时,该对象应该可以使用了。 That means the constructor should perform any initialization that is necessary. 这意味着构造函数应该执行任何必要的初始化。

What your friend suggests leads to unmaintainable and error-prone code, and goes against every good C++ practice. 你的朋友建议你导致不可维护和容易出错的代码,并违背每一个好的C ++实践。 He is wrong. 他错了。

As for your dynamic arrays, use std::vector instead. 至于动态数组,请改用std::vector And to initialize it, simply pass a parameter to the constructor: 要初始化它,只需将参数传递给构造函数:

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

will create a vector of 10 integers, all of them initialized to the value 20. 将创建一个10个整数的向量,所有这些都初始化为值20。

Not a complete answer, just my 2 cents: 不是一个完整的答案,只是我的2美分:

1: I would use new in the constructor, although for dynamic arrays, STL is the way to go. 1:我会在构造函数中使用new,尽管对于动态数组,STL是要走的路。

2: the normal error-handling for new is to raise an exception, so checking the returned pointer is useless. 2:new的正常错误处理是引发异常,因此检查返回的指针是没用的。

3: don't forget the new-operator to make the story a little bit more interesting. 3:不要忘记新的操作员,让故事更有趣。

Short answer : 简短回答
No, your friend is wrong. 不,你的朋友错了。 The constructor is where you do allocation + initialization. 构造函数是您进行分配+初始化的地方。 We even have a term called "Resource Acquisition is Initialization" (RAII)... classes acquire resources as part of their initialization in the constructor and classes free those acquired resources in their destructors. 我们甚至有一个称为“资源获取是初始化”(RAII)的术语......类在构造函数中获取资源作为其初始化的一部分,并且类在其析构函数中释放这些获取的资源。

Long answer : 答案很长

Consider the following code in a constructor: 在构造函数中考虑以下代码:

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

Now, suppose in the above that the second allocation were to throw an exception, either because the construction of whatever2 failed and threw an exception, or the allocation failed and threw std::bad_alloc. 现在,假设在上面的第二个分配是抛出一个异常,因为what2的构造失败并抛出异常,或者分配失败并抛出std :: bad_alloc。 The result is that the memory that you've allocated for whatever1 will have been leaked. 结果是你为whatever1分配的内存将被泄露。

For this reason, it is better to use a container class such as std::vector: 因此,最好使用容器类,如std :: vector:

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

When using type std::vector, if the second allocation fails, the destructor of the previous std::vector will be invoked, causing the resources to be appropriately released. 当使用类型std :: vector时,如果第二次分配失败,将调用前一个std :: vector的析构函数,从而导致资源被适当地释放。 This is probably what your friend meant when he said you shouldn't be using new (you should be using a container class, instead), in which case he would be mostly correct... although there are smart pointer classes such as boost::shared_ptr which provide you with these same safety guarantees and where you would still need to use new , so he still isn't quite right. 这可能是你的朋友在他说你不应该使用new时的意思 (你应该使用容器类),在这种情况下,他将大部分都是正确的......虽然有一些智能指针类,如boost: :shared_ptr为您提供这些相同的安全保证,并且您仍然需要使用new ,所以他仍然不太正确。

Note that if you have only one object/array that you are allocating, then this is not an issue... which is the way things are in your code... you don't have to worry about leaks due to some other allocation failing. 请注意,如果您只分配了一个对象/数组,那么这不是问题......这就是代码中的内容......您不必担心由于某些其他分配而导致的泄漏失败。 Also, I should add that new will either succeed or throw an exception; 另外,我应该补充一点, 新的意志要么成功要么抛出异常; it will not return NULL, and so that check is pointless. 它不会返回NULL,因此检查毫无意义。 The only instance where you should catch std::bad_alloc is in the case where you have been forced to do several allocations (you are forbidden from using std::vector), in which case you deallocate the other resources in the handler, but then you rethrow the exception, because you should let it propagate. 你应该捕获std :: bad_alloc的唯一实例是你被迫做几次分配(你被禁止使用std :: vector),在这种情况下你释放处理程序中的其他资源,但是你重新抛出异常,因为你应该让它传播。

Your friend is somewhat right. 你的朋友有点对。 However, it is common practice to do memory reservations in the Constructor, and deallocations in the Destructor. 但是,通常的做法是在构造函数中执行内存预留,并在析构函数中执行释放。

The problem with methods that may fail in Constructors is as follows: The constructor has no traditional means of notifying the caller of the problem. 构造函数中可能失败的方法的问题如下:构造函数没有传统的方法来通知调用者问题。 The caller expects to get an object instance that is not corrupt in any way. 调用者希望以任何方式获得一个没有损坏的对象实例。

The solution to this problem is to throw/propagate an exception. 此问题的解决方案是抛出/传播异常。 The exception will always get through and can be handled by the caller. 异常将始终通过,并且可以由调用者处理。

  1. If you are looking out for a return type ie if the function has to return a status then use the separate function (init()) to allocate memory. 如果您正在寻找返回类型,即如果函数必须返回状态,则使用单独的函数(init())来分配内存。

    If you check are going to check whether the memory got allocated by checking the NULL condition in all the member functions then allocate memory in the constructor itself. 如果您检查是否要通过检查所有成员函数中的NULL条件来检查内存是否已分配,则在构造函数本身中分配内存。

  2. The exception handling (ie try...catch) is a better choice. 异常处理(即try ... catch)是更好的选择。

  3. Apart from calling the constructor the "this" pointer is also initialized. 除了调用构造函数之外,“this”指针也被初始化。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM