user2599140
user2599140

Reputation: 474

unique_ptr becomes empty after leaving default constructor

This is a standard pimpl in VS 2013:

.h:

#pragma once

#include<string>
#include<iostream>
#include <memory>

class TestClass01
{
    private:    
        class impl; 
        std::unique_ptr<impl> pimpl;

    public:

        TestClass01();
        TestClass01(int num, std::string str);
        TestClass01(int num);
        TestClass01(std::string str);

        virtual ~TestClass01();

        int ReturnMyInt();
        std::string ReturnMyString();
};

.cpp

#include "stdafx.h"

#include "TestClass01.h"

#include <iostream>

class TestClass01::impl
{   
public:
    int myint;
    std::string mystr;

};


TestClass01::TestClass01() 
{ 
    pimpl = std::make_unique<impl>();   
}


TestClass01::TestClass01(int num, std::string str)
{
    //pimpl = std::make_unique<impl>();
    TestClass01();
    pimpl->myint = num;
    pimpl->mystr = str;
}

TestClass01::TestClass01(int num)
{
    TestClass01();
    pimpl->myint = num;
}

TestClass01::TestClass01(std::string str)
{
    TestClass01();
    pimpl->mystr = str;
}

TestClass01::~TestClass01()
{
    std::cout << "Destroyed TestClass01 with int=" << pimpl->myint << " and str=" << pimpl->mystr;
}

int TestClass01::ReturnMyInt()
{
    return pimpl->myint;
}

std::string TestClass01::ReturnMyString()
{
    return pimpl->mystr;
}

The problem with this code is that it crashes if I call the default constructor from any other constructor instead of directly instanciating impl:

TestClass01::TestClass01(int num, std::string str)
{
    //pimpl = std::make_unique<impl>(); -> This works, the line below doesn't
    TestClass01();
    pimpl->myint = num;
    pimpl->mystr = str;
}

In the line below TestClass01(); pimpl is empty. But setting a breakpoint in the default constructor reveals that pimpl points to an object in the default constructor, becoming empty only when it leaves it.

What's causing pimpl to become empty? It's a member variable and it shouldn't get out of scope (and therefore cause unique_ptr to delete the containing object).

Upvotes: 0

Views: 248

Answers (2)

Wintermute
Wintermute

Reputation: 44043

The proper syntax for constructor delegation in C++11 is

TestClass01::TestClass01(int num) : TestClass01() // <-- here, in the ctor
{                                                 //     init list.
  pimpl->myint = num;
}

VS 2013 supports that, so this should work for you. The way you're doing it now just creates a nameless temporary TestClass01 object that's immediately destroyed and doesn't affect the current *this at all.

Upvotes: 4

Mark Ransom
Mark Ransom

Reputation: 308176

You can't call a constructor directly. When you try, all you're doing is creating an unnamed temporary that disappears after the call, and it doesn't affect the current object at all.

Here's some code that demonstrates it:

struct test
{
    test() { cout << "default constructor on " << this << endl; }
    test(int) { cout << "int constructor on " << this << endl; test(); }
};

int main() {
    test t(1);
    cout << "test object at " << &t << endl;
    return 0;
}

See a demo at http://ideone.com/n2Thrn. The object pointers are output, notice that they're different.

Upvotes: 2

Related Questions