3

I read this post about using placement new to reset a boost::shared_ptr whilst avoiding additional memory allocations, and assume that the same, if not similar, can be done for a std::unique_ptr? My question is when the std::unique_ptr is of type Base* and so can point to any Derived*, will the placement new work as intended if the Derived classes vary in size? Something like this maybe:

class Base
{
public:
  Base() {}
  virtual ~Base(){}
};

class Foo : public Base
{
public:
  Foo() : Base() {}
  virtual ~Foo(){}
  int a;
  int b;
};

class Bar : public Base
{
public:
  Bar() : Base() {}
  virtual ~Bar() {}
  int a;
};

int main()
{
  std::unique_ptr<Base> bp(new Bar());
  bp->~Base(); //edit: call destructor
  void* rawP = dynamic_cast<void*>(bp.release());//edit: cast to void*
  bp.reset(new(rawP) Foo()); 
  return 0;
}
nitronoid
  • 1,459
  • 11
  • 26
  • Note that you're missing the destructor call for the original `Bar` object. – Kerrek SB May 12 '17 at 22:07
  • So overwriting the memory which holds the `Bar` object doesn't eliminate the need to call it's destructor? – nitronoid May 12 '17 at 22:08
  • 1
    This is worrying in principle: The returned `Base` pointer may not be pointing at the beginning of the allocation. At the very least you should obtain the real address via `dynamic_cast(rawP)`. – Kerrek SB May 12 '17 at 22:08
  • 1
    Imagine the constructor contains your wife's cooking and the destructor contains the antidote. I mean, yes, you *can* choose to not call it.. – Kerrek SB May 12 '17 at 22:09
  • @KerrekSB I'm not sure I understand, why wouldn't it point to the start? Where else would it point to? Also I have edited in the destructor, thanks it was silly – nitronoid May 12 '17 at 22:14
  • 1
    @KerrekSB makes a good point. Consider `class Baz : public Base2, public Base`. A cast pointer from `Baz*` to `Base*` won't have the same address. – Mark Ransom May 12 '17 at 22:32
  • 1
    @nitronoid: Why *would* it? `rawP` points to *some subobject* of the complete object; what makes you believe that subobject is at the front? – Kerrek SB May 12 '17 at 22:34
  • Ah I see, that makes a lot of sense, thanks for the explanations. I don't suppose casting to `void*` is guaranteed to point to the start? – nitronoid May 12 '17 at 22:39

1 Answers1

5

This isn't going to work. A Foo object is simply too big to fit in the memory allocated for a Bar object. If you want bp to point to a Foo object, you're going to have to allocate enough space for one.

Placement new constructs an object in memory you already own. It's on you to make sure that memory is big enough to hold the object you're constructing.

Miles Budnek
  • 28,216
  • 2
  • 35
  • 52
  • So if all my derived objects are the same size it would work? Also would there we a way to use the already allocated memory + however much more is required, rather than having to allocate an entirely new block? – nitronoid May 12 '17 at 22:29
  • @nitronoid I would expect that it would work if all of your derived classes are the same size. You'll probably want to cast to `void*` to make sure you're pointing at the beginning of your memory block as mentioned by @KerrekSB in the comments. Also no, there's no way to spread an object across two blocks of memory. – Miles Budnek May 12 '17 at 22:34
  • @nitronoid once a block of memory has been allocated for an object, there's no way to make that block larger. – Mark Ransom May 12 '17 at 22:35