0

Again with placement new I've found an example on this forum like this:

char *buf  = new char[sizeof(string)]; // pre-allocated buffer
string *p = new (buf) string("hi");    // placement new
string *q = new string("hi");          // ordinary heap allocation
  • But I think here buf is a pointer to an allocated and Constructed dynamic array of default-init characters. So the characters in the array are default initialized and have an indeterminate values.

  • I guess using the placement new in the second line will constructs objects on the previously constructed array of objects.

  • Why the user didn't call operator new on the array allocation rather than using new expression?:

     char *buf  = static_cast<char*>(operator new[](sizeof(string)));
    
  • After all I think if buff is a pointer to a dynamic array of non-default-constructible objects then the code will fail to compile using the new expression rather than using the operator new function.

  • Are my guesses correct?

Here is the link to the original answer:

What uses are there for "placement new"?

Itachi Uchiwa
  • 3,044
  • 12
  • 26
  • `new char[sizeof(string)]` - what do you think you are doing there? (I'm assuming `using namespace std;`) - "_I think here `buf` is a pointer to an allocated and Constructed dynamic array of objects_" - Why do you think that? – Ted Lyngmo Jun 04 '21 at 23:51
  • Point 1 touches on "What is an object?" Specifically "Is an array element really an object?" [Good reading that hopefully will answer such questions](https://en.cppreference.com/w/cpp/language/object) – user4581301 Jun 05 '21 at 00:02
  • I assume allocating a dynamic array of characters. – Itachi Uchiwa Jun 05 '21 at 00:07
  • ... of `char`, right? – Ted Lyngmo Jun 05 '21 at 00:07
  • @TedLyngmo: Yes. so the characters are default-initialized. Why we didn't use operator new() rather than new expression? – Itachi Uchiwa Jun 05 '21 at 00:09
  • Why do you think they are default initialized? (hint - they are not) – Ted Lyngmo Jun 05 '21 at 00:09
  • 1
    @TedLyngmo: Because I think new expression does the three tasks: allocation, construction and finally returns a pointer to the newly allocated and constructed memory. Doesn't it? – Itachi Uchiwa Jun 05 '21 at 00:11
  • 1
    Typical operator new: `void * operator new(size_t size) { void * p = malloc(size); return p; }` Yeah, it's default initialization aka "no operation". New expression calls operator new as its part but it's also offers either constructor call or initialization you provide, assuming default constructor or default initialization. With dynamic memory latter equals to noop. – Swift - Friday Pie Jun 05 '21 at 00:12
  • @ItachiUchiwa Where does initialization fit in? `new char[sizeof(string)]` allocates an array, most probaby 4 or 8 bytes long, uninitalized. – Ted Lyngmo Jun 05 '21 at 00:12
  • 1
    @TedLyngmo "*`new char[sizeof(string)]` allocates an array, most probaby 4 or 8 bytes long*" - `sizeof(string)` is a lot more than 4/8 bytes, especially if it implements SSO – Remy Lebeau Jun 05 '21 at 01:03
  • 2
    @RemyLebeau You are most probably correct. I was off. The standard allows `sizeof(string)` to be anything byte sized. It's not that it _must be_ more than 4 or 8. It usually is. Edit: (it's not - at all really) I can't dig myself out of this grave. I was totally wrong. I do know what a `string` looks like but had a brain freeze. – Ted Lyngmo Jun 05 '21 at 01:14
  • 1
    @eerorika <===== I'm exiting stage left :-) – Ted Lyngmo Jun 05 '21 at 01:44
  • @eerorika there are plenty of those because some industries are stuck with C++98 or C++03 and ether can't change or change only if their monopolistic hardware vendor would change (some avionics, embedded, nav systems, etc.). And for some platforms C++17 or 20 won't work effectively, ever. – Swift - Friday Pie Jun 07 '21 at 12:30
  • @Swift-FridayPie Would you say that there are plenty enough of those users that it's more **probable** that it applies to OP? *Possible*, I would agree. – eerorika Jun 07 '21 at 12:50
  • @eerorika I think portability here is at war against error cost and testing cost. One doesn't want a software bug that can have kill people by hundreds if not millions and I recently was a witness of one uncovered (thankfully, during testing) that could and it was a result of "porting" developers to C++11. – Swift - Friday Pie Jun 07 '21 at 13:16
  • @Swift-FridayPie I don't think that "OP probably uses old ABI" could be deduced from that argument. – eerorika Jun 07 '21 at 13:28
  • 2
    @TedLyngmo: you appear to be misunderstanding the subtle differences between the [new operator vs operator new](https://stackoverflow.com/questions/1885849/difference-between-new-operator-and-operator-new). Also, `new char[sizeof(string)]` does indeed default initialize. It's merely that the default initialization of `char` is a noop. You were probably thinking of "value initialization", which it does not do. – Mooing Duck Jun 07 '21 at 15:12
  • @MooingDuck Yep, I had all kinds of meltdowns that day. – Ted Lyngmo Jun 07 '21 at 15:21
  • @MooingDucjk I think, that difference had changed between C++03 and 11, 03 ditched zero initializing, I believe, C++11 seemed made difference between non-trivial class initialization and POD initialization, the latter is no longer "default" – Swift - Friday Pie Jun 07 '21 at 15:21

4 Answers4

2

Why the user didn't call operator new on the array allocation rather than using new expression?:

We cannot answer that question because we aren't that user. You should ask that from the user - though given that the example was written in 1998 it might not be easy to contact them. My guess: They didn't know that non-placement operator new exists or they didn't know what it is used for. Reusing the memory of an array of char is an intuitive choice in such case.

Note that the example of creating a singular dynamic std::string object makes little sense in the first place (I'm assuming that's what string in the example is).

I have a similar question to you: Why are you using operator new[] in your suggestion and not operator new? Even more importantly, why not use an allocator?

Are my guesses correct?

  1. Correct.
  2. Correct.
  3. This is a question and not a guess. I covered it above.
  4. It would fail. But that's irrelevant since char is default constructible.
eerorika
  • 232,697
  • 12
  • 197
  • 326
2

char is an object type that is both fundamental and trivial. Creating one doesn't, in practice, touch memory, and making an array of them does not either.

char* foo = new char[10];

and

char *foo  = static_cast<char*>(operator new[](10));

end up doing exactly the same thing in machine code, except the second one is a lot more verbose.

There are some subtle differences in the abstract machine; in one a bunch of chars are created, in the other the other they are not on that line. Coming up with a case where that matters is going to require a fair bit of language lawyering effort (I am thinking disposal may be different, and some access might be different, especially in standard versions before c++ fixed the malloc problem).

After all I think if buff is a pointer to a dynamic array of non-default-constructible objects then the code will fail to compile using the new expression rather than using the operator new function.

Sure, but the cast would be code smell, and the point of buf is to be storage for the later placement new. I guess it already is,

void *foo  = operator new[](10);

is less bonkers.

Just because you can static cast does not mean you should.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
1

operator new[](sizeof(string)) that's something odd, its incorrect syntax for creating an object. In best case scenario it creates an object in memory implicitly (if operator new implemented as std::malloc call and object is a POD type), without initializing or constructing one. All you can do in that case is to static_cast<char*>(new string); The offered line just would create a string object in dynamic storage and then make it anonymous by replacing type of pointer by char*.

Thing is, for placement new buf is not necessary to point to dynamic memory. It can be a static buffer.It can be a pointer to memory location within a rather large storage used to store multiple objects, a memory pool. New object would constructed at given location.

Note that in case of placement new std::string's data storage is still behaves as it usually does - it allocates character data in dynamic memory. To use some memory pool, programmer should provide appropriate allocator and that's one of purposes for placement new operator.

Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42
  • 1
    `that's something odd, its incorrect syntax` Why do you say so? Why does it compile without warnings? – eerorika Jun 05 '21 at 01:48
  • @eerorika because it's a direct operator call and C++ allows that for implementation reasons. But this doesn't construct or initialize created object, It's undefined if direct call for a POD type would be same as new expression for same type or not or I at least can't find any reference that they are equal. E.g., a new expression could generate trap values used by compiler implementation. – Swift - Friday Pie Jun 05 '21 at 14:34
0

No, buf isn't an array of objects. It's an array of characters, so basically an array of bytes. And while it was allocated with an array new, its basically being used as a byte pointer.

The use of placement new is if you want to allocate an object at an exact location, but you want to do so following all the rules of C++ object allocation- so constructors called and vtables set up. The usual use case for this is if you're doing your own custom memory allocation and reusing existing memory addresses. Firmware may use this to reuse memory as a pool. Or an RTOS may use it so that it doesn't exceed memory restrictions for a task.

This is actually a poor example of how its used because of that. You'd never new an array then placement new into it. You'd have a pointer to a block of allocated memory lying around, and you'd use placement new into that.

Gabe Sechan
  • 90,003
  • 9
  • 87
  • 127
  • 4
    `No, buf isn't an array of objects. It's an array of characters`. That's a contradiction. Characters are objects too. – eerorika Jun 05 '21 at 01:44
  • 1
    `You'd never new an array then placement new into it.` That's exactly what many array based standard containers do (vector, string, deque) through the default allocator. That's what the default allocator does: It "news" memory and starts the lifetime of an array inside it (without constructing the elements). – eerorika Jun 05 '21 at 01:57
  • @eerorika No, in C++ characters are not objects, they're primitive types. There's a difference. ANd no, the standard allocator doesn't allocate a block of memory and immediately placement new into it. The standard allocator has a pool of memory and placement news into (a subset) of it. It would not do a standard new then a placement new, that's pointless. In fact it would never new an array of bytes, it would get them directly from the OS. And it gets them far in advance of when its needed, except in the case of the pool being exhausted. – Gabe Sechan Jun 05 '21 at 02:09
  • 2
    No, there is no difference. Objects of primitive type are still objects. Yes, the default allocator does allocate using operator new. No, allocators don't placement-new into the memory. The standard containers placement-new into the memory they get from the provided allocator. – eerorika Jun 05 '21 at 02:13
  • @eerorika Primitive types are not objects. Not in C++. In other languages, yes. Not here. – Gabe Sechan Jun 05 '21 at 02:13
  • 1
    In C++, objects of primitive type are objects. In some other languages, object may mean something else. – eerorika Jun 05 '21 at 02:14
  • @eerorika Sorry, you're just wrong on that. Object has a meaning in computer science, and that isn't it – Gabe Sechan Jun 05 '21 at 02:15
  • 2
    Object hat a very specific meaning in C++. It's different from the meaning that you may be familiar from the terminology of Object Oriented Programming paradigm for example. There is no one universal terminology that applies to all of computer science. – eerorika Jun 05 '21 at 02:16
  • @eerorika Sorry, a language doesn't get to override the meaning of a CS definition. Primitives are not objects. Period. And I'm done discussing it – Gabe Sechan Jun 05 '21 at 02:17
  • 1
    The meaning of object in C++ is not particular to C++, but is a very old meaning shared by many other languages. – eerorika Jun 05 '21 at 02:25
  • 2
    @gabe If you disagree with the C++ standard's definition of terms, I would advise against answering C++ questions using your personal alternative definitions. Especially without clarifying. – Yakk - Adam Nevraumont Jun 05 '21 at 02:27
  • @eerorika Primitive types are not objects, and referring to them as such merely causes confusion. Your discussion on allocators was positives. This part of the conversation serves only to confuse people. C++ does not get to redefine the meaning of the word object, and you have only served to reduce the clarity of discussion. – Gabe Sechan Jun 05 '21 at 02:27
  • @Yakk-AdamNevraumont I'm using the standard definition in the field, and among C++ programmers (which is what I've spent the majority of my career doing). Nobody I ever worked with would call a primitive type an object. Especially not in this case where its clearly being used as a byte pointer and not even as an array of characters – Gabe Sechan Jun 05 '21 at 02:29
  • 2
    @GabeSechan Quite on the contrary. Insisting that objects aren't objects because they're "primitive" is what causes confusion. I let this slide earlier, but I now must also correct you that there is no meaning for "primitive type" in C++. But I interpret that by "primitive type" you're referring to the fundamental types. – eerorika Jun 05 '21 at 02:32
  • 1
    Look, the C++ standard says what the word means to the C++ language. It disagrees with colloquial use. This does not mean you should insist on using the colloquial terms unqualified when talking about C++, especially when dealing with language corner cases like operator new. Specialization, object, trivial, piles of initialization phrases, class: they all have technical meaning in the context of the C++ language that differs from casual use among programmers. Know and accept this and avoid using the technical terms in ways that do not align *without disclaimers*, or confusion results. – Yakk - Adam Nevraumont Jun 05 '21 at 02:53
  • 1
    @GabeSechan The primitive vs fundamental thing isn't bad since I doubt it'll lead to much confusion. But misunderstanding the meaning of "object" will make it harder to understand C++. For example: There are two categories of pointers: function pointers and object pointers. There are rules that apply to one that do not apply to the other. Former can point to functions and latter can point to objects... but then what is `char*` if `char` isn't a function nor an object? What rules apply to it? There's no confusion once one accepts that `char` is an object and thus `char*` is an object pointer. – eerorika Jun 05 '21 at 03:03
  • 2
    Redefine? C++ has used the term object as it does longer than you have been a programmer, and C defined it that way probably before you where born. The reason it is confusing when dealing with corner cases of languages is because you need to actually read the standard to understand what placement new over a buffer of char does. And that standard will talk about objects, which will include `char`s. When you refer to a `char` as an object, yes you should clarify, but you should also not say "there are no objects there" without reservation because *that can confuse someone reading the standard* – Yakk - Adam Nevraumont Jun 05 '21 at 04:07
  • @GabeSechan for a final nail into that coffin, references and non-static class member aren't objects in C++ even while they are variables, as it explicitly said in standard. Even while a non-static member might be of class-type. But an array is an object if its elements can be (thus, there is no array of references) – Swift - Friday Pie Jun 07 '21 at 12:02