-1

For some reason, I have decided to never use dynamic memory allocation in my program. This means all the variables in my program are static and the "new" constructor is never used. But the following code crashes, and produces a stack overflow exception:

VeryLargeObject x; // Global variable -> static memory 

void ResetTheObject() 
{
   x = VeryLargeObject(); 
}

Obviously, all I want to do is give x the default value of VeryLargeObject, which is a structure containing a lot of different variables, with their own constructors of varying complexity (so there's quite some initialization work to do). But here the language/compiler has decided that this should happen on the stack before being copied, and since VeryLargeObject is too large for the stack, my program crashes.

However I have found the solution to this problem:

VeryLargeObject x; 

void ResetTheObject() 
{
   new (&x) VeryLargeObject();  
}

I had never heard of this before, yet it does exactly what I want. This is a "placement new". It calls the constructor on the already allocated (or simply static) memory provided by a pointer.

My question, since I have the solution, is a rant: Why isn't this the default behavior of the first code ? If there isn't a less hacky way of doing this (i.e without the word "new" having anything to do with it), then why? Also why does it send me back the pointer, even though I just provided it? I thought C++ was a great language, but this seems kind of ugly and not very well-thought-out.

unknown
  • 368
  • 4
  • 12
  • 2
    "For some reason, I have decided to never use dynamic memory allocation in my program." "I thought C++ was a great language, but this seems kind of ugly and not very well-thought-out." don't you see connection btw them? – Slava Oct 26 '17 at 20:34
  • 5
    "*This means all the variables in my program are static*" No, it doesn't *mean* that. You really don't want a load of globals. – juanchopanza Oct 26 '17 at 20:34
  • 4
    You must not use placement new that way. You would first need to destroy the original object by calling it's destructor. – François Andrieux Oct 26 '17 at 20:34
  • Does `VeryLargeObject` follow the rule of 5? Does it have a move constructor and a move assignment operator? – wally Oct 26 '17 at 20:37
  • Unless you ask the standardization committee, I don't think you'll get a satisfactory answer to this question. – François Andrieux Oct 26 '17 at 20:37
  • No it has none of the gadget stuff (rule of 5, destructor). I guess I'm doing "C with objects" rather than C++, indeed. :) – unknown Oct 26 '17 at 20:41
  • 2
    *"I thought C++ was a great language, but this seems kind of ugly and not very well-thought-out."* I find it odd that this particular thing is what you find ugly about c++. – François Andrieux Oct 26 '17 at 20:41
  • Sometimes a placement new [doesn't return the same pointer](https://stackoverflow.com/q/15254/1460794) that was provided. – wally Oct 26 '17 at 20:41
  • The solution you found is not bad. It might even be ok not to call the destructor. If `VeryLargeObject` is just an aggregate then following the rule of 5 would probably not help. But that is one more reason why a [mcve] would help to answer this question better. Also I hear Python has a beautiful syntax and is much easier on the eyes. :) – wally Oct 26 '17 at 20:45
  • @demanze I'm curious why you won't use dynamic allocation. If it's the syntax or complexity, you can just use the standard containers and even standard smart pointers for all of your dynamic allocation needs. – François Andrieux Oct 26 '17 at 20:51
  • The project in question is a videogame, so I want everything to run as fast as possible in real-time with very little CPU overhead. And I wanted to know how the code would look with zero explicit dynamic allocations and zero strings. It turns out that most parts of the code are cleaner, better defined and easier to manage. It does require a lot of knowledge and discipline, but so far I'd say it's worth it. – unknown Oct 26 '17 at 20:53
  • I don't like relying on a OS call for small/medium operations. I understand why dynamic allocation is almost necessary at a certain (big) scale, but I don't like the idea of doing it repeatedly (thousands of times per second), which is for example what strings do everytime they construct / deconstruct / alter themselves. I think dynamic allocation is used a lot because of dogma and high level languages, but the alternatives are more attractive to me and perhaps ultimately superior. The only reason I use C++ and not plain C is OOP. – unknown Oct 26 '17 at 21:08
  • "I have decided to never use dynamic memory allocation in my program" -- premature optimization! – 2785528 Oct 26 '17 at 23:18

1 Answers1

1

First of all, turning on optimization might get you what you want with the first syntax. Without it, here's what you asked the compiler to do:

  1. Create a temporary object of type VeryLargeObject.
  2. Assign that into a global variable called x.

Since temporary objects need storage, the compiler allocates them on the stack. What the compiler is doing is, literally, what you asked the compiler to do.

The compiler may, if optimizations are turned on, understand that what the sequence is and save the copy. This requires that the compiler can positively prove to itself that the old value of x will not get in the way in any way. Since you admit that the initialization is quite complex, you can forgive the compiler if it did not manage to do so.

You have two options. You can either create an in-place initialization function and call that instead of the constructor, or you can use placement new, like you did.

The danger with placement new, as you used it, is that it replaces the old value of x without properly destructing it. It simply assumes that x is uninitialized. If that's okay for your use, then go ahead and use it. The compiler, for its part, is not allowed to assume that.

Shachar Shemesh
  • 8,193
  • 6
  • 25
  • 57
  • 1
    It's only okay if `x` is POD. The safest approach is to just call the destructor on the old instance before preforming placement new. If it's POD, the destructor will probably be an empty operation anyway. If it isn't, you'll be glad you called it. – François Andrieux Oct 26 '17 at 20:42
  • I thought too optimization might help, but I switched from O2 to O4 and it's the same. Indeed VeryLargeObject has a huge amount of objects to initialize and the compiler is simply too lazy to check that nothing gets in the way of the optimization. – unknown Oct 26 '17 at 20:46