0

First question, hope i managed to phrase it right.

While checking to see if i can "force" a buffer to behave as a class (with inheritance and vptr) , i tried the following code:

I defined the following classes:

class a
{
    public:

    char strBuff[50];
    virtual void boo() = 0;

};

class b : public a
{
    public:
    virtual void boo()
    {
        printf("%s\n", strBuff);
    }
};

And used them like this:

int main()
{
    char buffer_allo[100] = "123456789abcdefghijklmnopqrstuvwxyz123456789abcdefghijklmnopqrstuvwxyz";

    b* obj_Ptr = (b*)(buffer_allo);

    // Placement new
    new (obj_Ptr) b();

    // Calling virtual function
    obj_Ptr->boo();

    return 0;
}

The output of the program is empty, as in a \0 buffer, and i couldn't exactly figure out why.

I thought it was some thing of a default-initialization\ value-initialization problem, and indeed changing the placement new from new (obj_Ptr) b(); to new (obj_Ptr) b; didn't give a blank output, but a buffer where just the first 8 characters were overwritten (Probably by the vptr),

But then, i tried adding a constructor to either class, and discovered that adding an empty constructor to class b, will also prevent the buffer from being initialized with \0, but adding a constructor to class a seems to have no effect.

Why is this happening? is it really an initialization issue? and if so, why does adding an empty constructor solve it? or is it a compiler\c++ standard issue?

Tom Ronen
  • 1
  • 1
  • 1
    https://stackoverflow.com/q/620137/1171191 – BoBTFish Aug 15 '18 at 13:17
  • 2
    It should be `b* obj_Ptr = new (buffer_allo) b();` not that ugly cast – Slava Aug 15 '18 at 13:23
  • 1
    " is it really an initialization issue?" no it is issue of you invoking UB – Slava Aug 15 '18 at 13:26
  • If the `buffer_allo` is not correctly aligned for `b` then there is also UB – M.M Aug 15 '18 at 13:40
  • You can't "reuse" the original string, if that is what you are looking for. – Passer By Aug 15 '18 at 13:59
  • Point taken @Slava, it is an ugly cast, but how is it UB? if you too are referring to the size mismatch between the buffers, it was just an example, to see if such a thing is possible. – Tom Ronen Aug 15 '18 at 14:25
  • @PasserBy are you sure? because removing the parentheses and using the new seems to work, as referenced by the similar question – Tom Ronen Aug 15 '18 at 14:26
  • @TomRonen Different context, same deal: https://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope/6445794#6445794 – Passer By Aug 15 '18 at 14:45
  • @TomRonen you are reading from unintialized member - that is UB – Slava Aug 15 '18 at 14:59
  • @TomRonen There is nothing in your code that legitimately initialises `strBuff`. I guess you're expecting it to have the content of `buffer_allo` but nothing in C++ says that will be the case. – john Aug 15 '18 at 15:43

1 Answers1

0

Well, it is that dark area of C++ initialization rules. They are quite intricate and you can read them in cppreference.

Basically, when you call placement new - new (obj_Ptr) b(); (with an empty pair of parentheses) value initialization takes place. According to the standard:

1) if T is a class type with no default constructor or with a user-provided or deleted default constructor, the object is default-initialized;

2) if T is a class type with a default constructor that is neither user-provided nor deleted (that is, it may be a class with an implicitly-defined or defaulted default constructor), the object is zero-initialized and then it is default-initialized if it has a non-trivial default constructor;

3) if T is an array type, each element of the array is value-initialized;

4) otherwise, the object is zero-initialized.

Since it is zero-initialized this is what you see when you call obj_Ptr->boo();. When you define you own constructor the object is default-initialized, and hence your buffer contains garbage. This is basically it. More detailed explanation: value initialization in cppreference

Also, not related to the question, but I would like to mention a couple of things.

1) As Slava pointed out, it is better to use return from placement new directly, without any casts, like this: auto obj_Ptr = new(buffer_allo) b();

2) Try to avoid keyword virtual for the method of derived class. It is better to use override, since it is clearer and can catch mistakes when you accidentally create new function in the derived class instead of overriding the one in the base. Like this: void boo() override{...}