2

Hello I am on chapter 19 from C++ primer 5th edition. 'Operator new and delete vs new and delete expression and placement new':

AFAIK operator new and operator delete allocate and deallocate memory respectively but don't construct an object there. On the other hand a new-expressioncalls operator new to allocate memory, construct an object in that memory address and finally returns a pointer to the newly allocated and initialized object.

So for the understanding sake I've tried this:

struct Foo{
    Foo(){std::cout << "Foo()\n";}
    ~Foo(){std::cout << "~Foo()\n";}
    void bar() const{
        std::cout << "Foo::bar()\n";
    }
};

int main(){


    Foo* pf = (Foo*)operator new(sizeof(Foo)); // Foo() is not called! Is it UB?
    pf->bar(); // looks to work fine. Does it really?
    delete pf; // looks fine?


    Foo* pf2 = new Foo{}; // ok Foo() is called 
    pf2->bar(); // OK
    delete pf2; // OK ~Foo() is called

}

As you can see in the first example I've called operator new directly, and I guess this latter does allocate memory only but doesn't construct an object there and finally it returns a pointer to void pointing to the newly allocated member.

So I don't get the constructor called Foo(). Now why calling a member function works fine? pf->bar()? or does this yield an undefined behavior?

The second example (pf2) is straightforward.

  • If one is safe and correct, what happened? how could I use an uninitialized object? Thank you!
user207421
  • 305,947
  • 44
  • 307
  • 483
Itachi Uchiwa
  • 3,044
  • 12
  • 26

2 Answers2

2

Unless you use placement new (which is basically the other half of the ordinary new), no Foo object exists, so you can't call a member function on it. Moreover, if you do use placement new, you must call the destructor yourself:

Foo* pf = new (operator new(sizeof(Foo))) Foo;  // no cast needed
pf->bar();
pf->~Foo();
operator delete(pf);  // analogous, though not equivalent, to std::free
Davis Herring
  • 36,443
  • 4
  • 48
  • 76
  • So you mean for the purpose of separating allocation from initialization I can do: `Foo* pf = (Foo*)operator new(sizeof(Foo)); pf = new(pf)Foo{}; pf->bar(); pf->~Foo(); operator delete(pf); // analogous, though not equivalent, to std::free` I called operator new first to get memory then used placement new to construct an object there. So is it safe now? – Itachi Uchiwa May 31 '21 at 23:30
  • 1
    @ItachiUchiwa: Yes, which is exactly how François Andrieux put it except with a separate `void*` to, again, avoid the cast. – Davis Herring May 31 '21 at 23:31
  • Thank you so much! it was really clear now. – Itachi Uchiwa May 31 '21 at 23:33
1
  • Now why calling a member function works fine? pf->bar()? or does this yield an undefined behavior?

This is Undefined Behavior. pf is a Foo* that does not point to an instance of Foo so you cannot dereference it. It looks like it works for the same reason using a pointer after you delete it might look like it works. In this case, since bar() doesn't access this, it is similar to a static member function and the observed behavior will likely be that it will print the message. But you can't rely on this, the behavior may change for no obvious reason. You shouldn't expect C++ to complain or otherwise diagnose when you have Undefined Behavior.

  • delete pf; // looks fine?

delete pf; is not fine because pf was not created by a new expression (calling operator new does not constitute a new expression). You need to use operator delete to free the memory. This is again Undefined Behavior.

  • If one is safe and correct, what happened? how could I use an uninitialized object?

Case #1 is not safe and not correct. You cannot use an uninitialized object. You can initialize it using placement new :

 void * memptr = operator new(sizeof(Foo));
 Foo* pf = new (memptr) Foo;

You still can't use delete here to destroy the object created by a placement new expression. You need to call the destructor explicitly, then use operator delete to separately free the memory.

pf->~Foo();
operator delete(memptr);
François Andrieux
  • 28,148
  • 6
  • 56
  • 87