Alloc memory in constructor or init function?

Viewed 242

I'm new to C++, I have a class hold some memory, the class looks like:

class MyClass
{
public:
    MyClass (int s)
    {
        if (s <= 0) _size = 1;
        else _size = s;

        data = new int[_size];  // may throw exception
    }


private:
    int *data;
    int _size;
};

To my knowledge, throw exception in constructor is unsafe, so I put the malloc to a init function.


class MyClass
{
public:
    MyClass (int s)
    {
        if (s <= 0) _size = 1;
        else _size = s;
    }

    void init()
    {
        data = new int[_size];
    }


private:
    int *data;
    int _size;
};

my problem:

  1. memory alloc in constructor or init function, which is better
  2. if I chose init function, how to ensure init function has called before call other member function?
4 Answers

To my knowledge, throw exception in constructor is unsafe, so I put the malloc to a init function.

No, it is not "unsafe". It's the way to fail a constructor. You may be thinking about destructors aborting or other issues with exception safety guarantees.

"Init" functions introduce a two phase approach to constructors that increases complexity. Unless you need to work in an environment without exceptions, avoid them. Factory functions are another way to do things in that case.

memory alloc in constructor or init function, which is better

Constructor. See std::vector.

if I chose init function, how to ensure init function has called before call other member function?

Statically you cannot without a run time check or external tools.

You can set a flag on your init function and then check in every member function.

if I chose init function, how to ensure init function has called before call other member function?

You can't, and this is a big problem! What you have stumbled on here is a concept called Resource Acquisition is Initialisation (RAII). This is roughly what you have written in part 1. If you add a destructor:

~MyClass() {
    delete[] data;
}

Then you have started working towards one of the most useful c++ containers, the std::vector. This does a similar thing to what you are trying to achieve here.

There is even a cpp core guideline about this: A constructor should create a fully initialised object.

To my knowledge, throw exception in constructor is unsafe

Actually this is untrue. In fact the very next core guideline is If a constructor cannot construct a valid object, throw an exception.

TLDR: Use option one, init is bad in this case.

The point in constructors are that you can be sure that the constructor is called before any other member function.

Init functions come from c and are not considered best practice

In addition the valid answers above, you might consider "encapsulating" the allocation and de-allocation of memory using an existing standard library class: std::unique_ptr<int>. Using it may save you the need to implement a bunch of methods (e.g. copy constructor, move constructor, assignment operator and destructor; see the rule of five).

Also, std::size_t is more idiomatic for storing amounts of allocated memory.

Finally, names starting with _ are generally reserved, and we avoid them.

So:

#include <memory>

// ...

class MyClass {
public:
    using size_type = std::size_t;
    using value_type = int;

    MyClass (size_type size) :
        size_(std::max<size_type>(size,1)),
        data_(std::make_unique<value_type[]>(size_))
    { }

    // etc. etc. - but no need for copy ctor, move ctor, assignments and dtor

private:
    std::unique_ptr<value_type[]> data_;
    size_type size_;
};
Related