2

I need to be sure that a constructor does not initialize some member variables:

struct A{
    int a, b;
};
struct B: A{
    int c;
    B():c(42){}
}

char data[sizeof(B)];
B *v=reinterpret_cast<B*>(data);
v->a=42;
v->b=42;
new (v) B;

I want A to be defined in such a way that the placement new operator does not modify v->a and v->b. How should I do that?

A nasty hack would be to store the needed values of v->a and v->b in thread-local variables and then copying them in A::A(). However this won't work if the constructor of B creates another A so I'll need a stack of them. Is there a faster way?

Hristo Venev
  • 972
  • 5
  • 17
  • As of C++20, you can use `reinterpret_cast` to create suitably trivial objects with no constructor called at all; this piecemeal approach just isn't C++. – Davis Herring Jun 20 '23 at 08:53

2 Answers2

1

You probably cannot guarantee that the "old" A is untouched without resorting to non-standard methods (aka "nasty hacks"), since in general a compiler is allowed to prepare your new, uninitialized A as it sees fit.

Here's my suggestion, which is however quite insecure, relying on undefined behaviour and with decent chances to work only if both A and B have standard layout (no virtual functions, etc.):

char data[sizeof(B)];
// initialize your A object at *data

class B_minus_A {
   int c;
}

new (v+sizeof(A)) B_minus_A;   // (beware the alignment issues!)

B *v=reinterpret_cast<B*>(data);

In alternative, an orthodix way is creating a constructor for B accepting an A argument, and passing it the A object that it will overwrite. This is guaranteed to work, the obvious disadvantage is that you pay the cost of two copies (from *data to the argument stack of B's constructor and vice versa), and you must be allowed to modify the source code of B.

Alberto M
  • 1,057
  • 8
  • 24
0

I assume you're working directly with hardware, or on some really high performance code to need to be doing this.

Placement new will call the constructors for A and B in that order. Since neither modifies a or b, you're safe. If your constructor modified either variable, you'd need to change the constructor.

Adam Leggett
  • 3,714
  • 30
  • 24
  • What about debugging tools like msan/asan? They might overwrite the values in the default constructor. – Hristo Venev Apr 04 '15 at 14:02
  • I haven't used msan/asan directly myself, but I doubt they will attempt to overwrite any memory during placement new. You'll have to test. Initialize your array to some value, and then call placement new. – Adam Leggett Apr 04 '15 at 14:05
  • Sadly gcc zero-initializes before placement new. – Hristo Venev Apr 04 '15 at 14:40
  • According to one answer at http://stackoverflow.com/questions/7546620/operator-new-initializes-memory-to-zero: In C++11, the language used is that the allocated objects are default-initialized which for non-class types means that no initialization is performed. This is different from the meaning of default-initialized in C++03. Try enabling -std=c++11 – Adam Leggett Apr 04 '15 at 19:56
  • I care about what gcc and clang do. And they might zero-initialize. – Hristo Venev Apr 05 '15 at 18:28
  • If you test it with `-std=c++11` and they follow the standard and don't do it now, I wouldn't expect them to revert to not following the standard later. – Adam Leggett Apr 06 '15 at 14:24
  • Sadly, when I tested gcc did zero-initialize. – Hristo Venev Apr 06 '15 at 15:11
  • I'm sorry to hear that. That appears to be in direct conflict with the standard. – Adam Leggett Apr 06 '15 at 15:17
  • 3
    @AdamLeggett No, it isn't. The compiler is free to clobber the memory as it sees fit, because no well-defined code can observe the value. – T.C. May 05 '15 at 16:35