0

Does std::make_shared value initalize members when there is a default ctor that fails to initailize its members?

I stumbled upon this when using a class where the default ctor did not initialize members using RAII style gave a warning and wondered why no warning was issued when it was used before elsewhere. Elsewhere it was using std::make_shared and I was thinking why it had been working before and no warning was issued.

#include <iostream>
#include <memory>

class Foo
{
public:
    Foo() {} // should initalize mode but does not

    int mode;

    bool isZero() const
    {
        if (mode == 0) {
            return true;
        }
        return false;
    }
};

int main(int argc, const char* argv[])
{
    auto blaa = std::make_shared<Foo>();
    if (blaa->isZero()) { // No warning. Always zero initialized because using make_shared?
        std::cout << "blaa.mode is zero\n";
    }
    Foo foo;
    if (foo.isZero()) { // warning: 'foo' is used uninitialized in this function - as expected
        std::cout << "foo.mode is zero\n";
    }
    return 0;
}
Allan Ojala
  • 694
  • 10
  • 22
  • It may zero out the memory. Most likely you don't get a warning because `make_shared` hides the actual object behind a void pointer. – NathanOliver Feb 10 '21 at 13:41
  • @NathanOliver dynamically allocated object (even with `new`) still whibit the same behavior. I would speculate that it could be due to the fact that compiler has a better hold on a lifecycle of an automatic object than dynamically allocated one. – SergeyA Feb 10 '21 at 14:05

2 Answers2

2

No, make_shared does not do anything different, with respect to constructing an object, than otherwise happens when an object gets constructed in any other way. All objects follow the same rules when they get constructed in C++.

Your constructor fails to initialize mode. Just because it happens to be zero, when it's constructed in dynamic scope, with your compiler, and your operating system: that doesn't mean anything, and this does not give you any guarantees as to what happens when the object gets constructed in automatic scope (or vice versa).

This is undefined behavior. As far as compiler warnings go: the compiler is not obligated to issue a warning message for undefined behavior. Any such warning message should simply be considered to be an unexpected bonus and a surprise. Your compiler does not detect undefined behavior in one of your two test cases, and that's simply how it is.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • Minor nit-pick, the compiler is obligated to issue a diagnostic message for undefined behavior in constexpr (and I think consteval) functions. Otherwise, +1 – NathanOliver Feb 10 '21 at 13:44
  • "All objects follow the same rules" ... except it's a relevant side note here that objects with static and thread storage duration are either constant-initialized or zero-initialized. Of course, no objects in this example have static or thread storage duration. – aschepler Feb 10 '21 at 13:58
  • Yet compilers are written by humans, and those humans actively try to make compilers smart enough to detect undefined behavior, so it would be interesting to understand why no warning is given in make_shared case. I do not think this answer is complete. – SergeyA Feb 10 '21 at 14:01
  • I tried with msvc 19.25.28614, gcc 9.3.0 and clang 11.0.1. None of them gave warning when using make_shared but using the RAII style msvc and gcc gave warnings. So that is what lead to the question does make_shared do magic initialization and thus no warnings. – Allan Ojala Feb 11 '21 at 09:07
0

With make_shared you are creating a shared pointer that retains the ownership of an object through a pointer. Which means that there is an internal counter which tracks this ownership. Here is a more detailed explanation: https://en.cppreference.com/w/cpp/memory/shared_ptr. Just as Sam Varshavchik mentioned in his answer make_shared will not do anything different in terms of initialization.

What you might be wondering is why there are different values for mode when you use make shared vs just a simple Foo declaration, like you see here in this picture: Locals view in Visual Studio

The 0xcdcdcdcd bit pattern indicates that this memory has been initialized by the memory allocator (malloc() or new) but has not been initialized by your software (object constructor or local code).

The 0xcccccccc bit pattern is used to initialize memory in data that is on the stack.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Bogdan Ariton
  • 89
  • 1
  • 7