Alloc memory in constructor or init function?
Asked Answered
A

4

5

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?
Amick answered 20/5, 2021 at 7:13 Comment(4)
Q2 - seems like you've answered your own question. Having a separate init() introduces other issues for you so don't do it!Bezant
"Throwing exceptions in constructor is unsafe", that's not true, throwing in constructors is not different to throwing in functions, if exceptions propagate outside of constructor body, the object is not created.Rescript
First and foremost: why even bother with naked pointer? Vector/smart pointer of some sort should work as good.Touchwood
To insist on the naked pointer problem, handling explicit dynamic memory in a class requires that if respects the rule of five. If it does not any implicit copy will lead to nightmares...Peyote
W
5

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.

Whyalla answered 20/5, 2021 at 7:20 Comment(2)
Thanks for reply! It seems that I have to work harder to learn C++.Amick
@Amick No problem! C++ is so complex we are all learning new things.Whyalla
A
2

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.

Additive answered 20/5, 2021 at 7:22 Comment(0)
D
1

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

Doha answered 20/5, 2021 at 7:20 Comment(0)
B
1

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_;
};
Basir answered 20/5, 2021 at 7:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.