0

Looks like std::make_unique is making extra copy(related post) while it is supposed to be more efficient. Below is a sample implementation with typical c++ polymorphic class architecture. The output mentioned below the code snippet shows if we uncomment the call to make_unique, it calls an extra pair of ctor/dtor.

class Shape
{
public:
    Shape() { cout << "Shape::ctor\n"; }
    virtual ~Shape() {cout << "Shape::dtor\n";}
};

class Rectangle : public Shape
{
public:
    Rectangle() {cout << "Rectangle::ctor\n";}
    virtual ~Rectangle() {cout << "Rectangle::dtor\n";}
};

unique_ptr<Shape> makeShape(int type) 
{
    //unique_ptr<Shape> spShape = make_unique<Shape>(); //extra ctor call
    unique_ptr<Shape> spShape(nullptr);
    switch (type)
    {
    case 1: //rect
        spShape.reset(new Rectangle());
        break;   
    default:
        break;
    }
    return spShape;
}

int main(int argc, char const *argv[])
{
    auto shape1 = makeShape(1);
    return 0;
}
==========================
output with make_unique
--------------------------
Shape::ctor
Shape::ctor
Rectangle::ctor
Shape::dtor
Rectangle::dtor
Shape::dtor
--------------------------
output w/o make_unique
--------------------------
Shape::ctor
Rectangle::ctor
Rectangle::dtor
Shape::dtor
--------------------------

make_unique doesn't works with custom deleter. So what is benefit of using it?

getsuha
  • 711
  • 6
  • 16
  • If you just want an empty `unique_ptr`, then just `unique_ptr spShape;` is enough. I don't really understand why you give it the `nullptr` argument or try to use `make_unique`. Or do you expect the pointer to point to a `Shape` object in the default case? – user17732522 Jan 12 '22 at 05:15
  • 1
    *"Looks like std::make_unique is making extra copy(related post) while it is supposed to be more efficient."* -- more efficient than what? Your code compares calling `make_unique`, which constructs a `Shape` object, to using `nullptr`, which does not. Of course there is an extra constructor call when you construct an extra object. Why did you think otherwise? – JaMiT Jan 12 '22 at 05:28

2 Answers2

5

The std::make_unique<Shape>() does not create a null unique_ptr. Instead, it is equivalent to this:

unique_ptr<Shape> makeShape(int type) 
{
    unique_ptr<Shape> spShape(new Shape); //extra ctor call
    switch (type)
    {
    case 1: //rect
        // Must destroy old spShape here.
        spShape.reset(new Rectangle());
        break;   
    default:
        break;
    }
    return spShape;
}

If you don’t want to create an object, don't use std::make_unique. The purpose of std::make_unique is to construct the object and make the std::unique_ptr in one step.

[make_unique] is supposed to be more efficient [...]

No, it is not. It is supposed to be safer, if you are constructing objects with temporaries. See:

Differences between std::make_unique and std::unique_ptr with new

Cleaned-up code:

I might write it this way:

std::unique_ptr<Shape> makeShape(int type) {
    switch (type) {
    case 1:
        return std::make_unique<Rectangle>();
    default:
        return std::make_unique<Shape>();
    }
}
Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
4

std::make_unique ... while it is supposed to be more efficient

std::make_unique isn't "supposed to be more efficient". It's a safe and convenient way to create objects in dynamic storage.

Looks like std::make_unique is making extra copy

It's because you're using it wrong. Do this:

switch (type)
{
    case 1: // sidenote: don't use magic numbers such as this
        return std::make_unique<Rectangle>();
    default:
        return nullptr;
}

what is benefit of using it?

Some benefits of using std::make_unique<T>() versus using std::unique_ptr(some_bare_owning_pointer) are:

  • Exception safety. If exception is thrown after memory was allocated, but before the constructor of std::unique_ptr is entered, then you leak the allocation. There are cases where this doesn't look possible to happen, but it subtly is (especially prior to C++17).
  • It lets you follow the rules of thumb "write one delete for each new" (write 0 news -> write 0 deletes) as well as "never use an owning bare pointer".
eerorika
  • 232,697
  • 12
  • 197
  • 326