-1

Here is my program:

#include <iostream>

using namespace std;

class Object {
public:
    Object() { cout << "Object constructor!" << endl; }
    ~Object() { cout << "Object destructor!" << endl; }
    Object(const Object& obj) { information = obj.information; cout << "Copy constructor!" << endl; }
    void setInformation(const string info) { information = info; }
    string getInformation() const { return information; }
private:
    string information;
};

class Storage {
public:
    Storage() { object = static_cast<Object*>(operator new(sizeof(Object))); }
    ~Storage() { operator delete(object); }

    void setObject(const Object& obj) {
        // Todo: assign obj to the allocated space of the pointer object
    }
private:
    Object* object;
};

int main()
{
    Object o;
    o.setInformation("Engine");
    Storage storage;
    storage.setObject(o);
    return 0;
}

In Storage I am allocating space to store one object of type Object without creating it. I am using placement new for that which allocates a memory, and freeing it in the destructor. I know that I can use

object = new(object) Object()

to construct an object. But can I put in the memory an object that is already created? In my case call method setObject(). If yes what problems I can encounter with such memory management? Thanks in advance.

Oleg
  • 1,027
  • 1
  • 8
  • 18
  • 1
    The problems here are numerous. Besides `Storage` violating the Rule Of Three itself, if `setObject` never gets called, the destructor attempts to `delete` something that wasn't constructed as an instance of the object. Kaboom. – Sam Varshavchik Oct 31 '18 at 11:04
  • You need to allocate your object somewhere, and there is no reason why the object from `setObject` was allocated on the stack and there is no way for you to retrieve it as it's const. So you can't assign it to your pointer. Use a unique_ptr to store the object and create a new one when you have a call to setObject. If you don't want to create a new obe, pass in a unique_ptr. Be explicit about your memory management. – Matthieu Brucher Oct 31 '18 at 11:05
  • Sam, I know that but thanks for pointing that out. The program is not finished yet. And that is why I decided to ask the specific question first: how to fill method setObject(). I know that I have to call destructor explicitly later. – Oleg Oct 31 '18 at 11:08
  • Are you limited to c++11? – r3mus n0x Oct 31 '18 at 11:17
  • No, I am not limited. As I understand this can't be done in C++ 11 right? – Oleg Oct 31 '18 at 11:21

3 Answers3

2

The simplest way to achieve the behavior you are trying to implement would be to use std::optional from C++17. It will allow you to "reserve" the memory for your Object without constructing it and without using the heap. It will also handle all the constructor and destructor calls.

This also can be done without std::optional, but you will essentially have to implement a similar solution yourself. In any case you will need an additional member to specify whether object constructed or not, so you can properly handle the destruction and assignment.

Then your setObject method will look something like this:

void setObject(const Object& obj) {
    if (constructed) {
        *object = obj;
    } else {
        new (object) Object(obj);
        constructed = true;
    }
}

To make use of move semantics you can modify your method like this:

void setObject(Object obj) {
    if (constructed) {
        *object = std::move(obj);
    } else {
        new (object) Object(std::move(obj));
        constructed = true;
    }
}

Note that now setObject accepts it parameter by-value so it can be used with both lvalue and rvalue references to construct the parameter which is then will be moved into the member.

r3mus n0x
  • 5,954
  • 1
  • 13
  • 34
  • Thanks, yes that I was suppose to do afterwards. – Oleg Oct 31 '18 at 11:32
  • @Oleg, it would still be better just to use `std::optional`, but if you want to do it yourself you can get rid of the dynamic allocation using a buffer `char buffer[sizeof(Object)]` or a more elegant solution would be to use a one member `union { Object obj; }`. – r3mus n0x Oct 31 '18 at 11:37
  • Am I correct that if I use the setObject that you have wrote I need to do next: if (constructed) (*object).~Object(); in the destructor? – Oleg Oct 31 '18 at 11:53
  • @Oleg, yes and you can use `object->~Object();` for better readability. – r3mus n0x Oct 31 '18 at 11:57
  • Is there a way to do the same thing if copy constructor is deleted? – Oleg Oct 31 '18 at 12:09
  • @Oleg, you can use move constructor if it's available. You'll need to change the signature of `setObject` to accept rvalue reference or accept by-value and then use `std::move` in the body. – r3mus n0x Oct 31 '18 at 12:12
  • Thanks, I wanted to understand memory management more deeply. But for sure I will use C++ 17 (std::optional) for this kind of problem. – Oleg Oct 31 '18 at 12:24
  • @r3musn0x `char buffer[sizeof(Object)]` is incorrect, since it does not guarantee proper alignment. Use, e.g., `std::aligned_storage` instead. – Daniel Langr Oct 31 '18 at 12:43
1

Placement new is a pretty advanced construct, and the thought of having an Object* that actually points to uninitialized memory is rather terrifying. May I suggest you make your life easier?

class Storage {
    void setObject(const Object& obj) {
        if (object) {
            *object = obj;
        } else {
            object.reset(new Object(obj));
        }
    }
private:
    std::unique_ptr<Object> object;
};

This requires a copy constructor and assignment operator on the Object class, but is much more idiomatic C++. And in the Object implementation as shown, the default, compiler-provided implementations are already fine.

Thomas
  • 174,939
  • 50
  • 355
  • 478
0

Just to clarify:

But can I put in the memory an object that is already created?

No, you cannot. There is no way how to move objects in memory in C++. For trivially copyable types, you can copy (e.g, by memcpy or memmove) their memory representations. But this actually does not move anything. Moreover, your Object class is not trivially copyable.

What you can move is the contents of objects, which is what move semantics is for.

Daniel Langr
  • 22,196
  • 3
  • 50
  • 93