-1

Is there any way to initialize value of member field of a class from it's overloaded new operator? I can set values of object in operator new, but after it returns my pre-initialized object, class constructor overrides all uninitialized values (at least, in debug compilation).

What I'm trying to do: allocator that uses vector of buckets of my objects, where newly created objects fill buckets one by one. Each bucket stores amount of filled slots and index of next slot that would be filled, allocator increases that amount and deleter decreases it. When amount reaches zero, it moves bucket to the end of vector of buckets, so when allocator fills currently filling bucket it either reuses that emptied bucket (and moves it into begin of vector) or creates and adds a new one. But for deleter to know which bucket's amount to decrease, it needs a pointer to the bucket where this object was placed (otherwise, I'd need to search each bucket in vector to find object being deleted, or map object pointers to buckets).

So, I'm trying to write current bucket pointer into my objects itself from allocator. But constructor overwrites it with nullptr.

Aberro
  • 620
  • 6
  • 10
  • 1
    thats a constructor not an overloaded `new`. It is not clear what you mean with "But this didn't worked, because value was initialized with default value.", because there is no "default value". Please post a [mcve] and explain how its output didnt fit your expectations – 463035818_is_not_an_ai Feb 07 '22 at 19:11
  • 2
    I suspect this could be an *outstanding* question if the specific use case were amplified. E.g. show the code that's using *this* code, that you hoped would work, but didn't (and further amplify where/how it didn't), including the overloaded new operator. – WhozCraig Feb 07 '22 at 19:12
  • I've expanded my question with intended scenario. – Aberro Feb 07 '22 at 19:21
  • It's still unclear what you're trying to do. Please provide the usage of `no_default`. – Stephen Newell Feb 07 '22 at 19:40
  • 2
    The variants of `operator new` all just allocate memory in preparation for an object to be constructed into it. This occurs before the constructor has begun, so there is no object yet for that operator to do any work on. If you are trying to `reinterpret_cast` the pointer it will return, it is Undefined Behavior to use the result. If you try to `memcpy` an initial memory representation, this won't have any effect as the member will still be considered uninitialized and a debug build is still allowed to initialize it with guard values if it wants, and reading it is still UB. – François Andrieux Feb 07 '22 at 19:40
  • perhaps you are looking for placement new ? https://stackoverflow.com/questions/222557/what-uses-are-there-for-placement-new – 463035818_is_not_an_ai Feb 07 '22 at 19:44
  • @StephenNewell is that better? no_default doesn't matter in this question, it's code was already it's own usage, I was just hoping that with it I could trick compiler to leave my field as it was after memory allocation, practically uninitialized (or in my case - initialized in ``operator new``). – Aberro Feb 07 '22 at 19:49
  • "_But for deleter to know which bucket's amount to decrease, it needs a pointer to the bucket where this object was placed_": If I understand correctly, your `operator new` should overallocate for this pointer, store the pointer at the beginning of the allocation and then return the pointer to the allocation offset by the pointer size, so that the user of `operator new` doesn't see the pointer as part of the allocation. Of course you need to assure correct alignment and implement `operator delete` accordingly. – user17732522 Feb 07 '22 at 19:51
  • @FrançoisAndrieux, well, there may be no object, but in my case I know which object I'm creating, and where it's fields are in memory, so technically, I can write values there. Or I could if compiler won't make an initializers for all fields (and I'm pretty sure it doesn't make them with optimization flags being set) – Aberro Feb 07 '22 at 19:52
  • @Aberro Using it that way would be undefined behavior according to the standard. – user17732522 Feb 07 '22 at 19:53
  • @user17732522 You should've write this as an answer. Because it is, at least I think it should work. – Aberro Feb 07 '22 at 19:55
  • @Aberro From your question it isn't really clear to me that it is the right answer. I am only guessing. I also suspect that your actual code has more problems that need to be worked around. – user17732522 Feb 07 '22 at 19:58
  • @Aberro If you don't initialize the member via a constructor, it is default initialized. For something like an `int` that means it is uninitialized. It doesn't matter that *you* know that you set the underlying representation. Strictly speaking, its state is officially unspecified and any attempt to read it is still Undefined Behavior. You cannot perform meaningful initialization of an object in its `operator new`. Unspecified doesn't mean "has whatever value that happens to be in memory at this time". It really means "doesn't have a value, you aren't allowed to try to read this". – François Andrieux Feb 07 '22 at 19:59
  • `operator new` returns fresh raw bytes. That's its purpose in life. You cannot make it return anything else. Raw bytes are not your object, you cannot return that from `operator new`. The constructor turns raw bytes into an object. You cannot make it *not* do that. – n. m. could be an AI Feb 07 '22 at 20:06

1 Answers1

0

If your object has a bucket ID/pointer as a member, it must be initialised in the object's constructor. No ifs, buts, or maybes here. One way to do this is as follows:

 auto [bucket, slotAddress] = findFreeSlot(sizeof(MyObject));
 MyObject* newObject = new (slotAddress) MyObject{bucket}; // placement new
 // optionally
 someSmartPtr<MyObject, CustomDeleter> ptr{newObject, customDeleter};

You don't need a custom overloaded operator new here.

If you absolutely positively must initialise the bucket in the overloaded operator new, do not make bucket a member of your object. Instead, allocate and initialise it as a piece of auxiliary information, much like standard operator new stores bookkeeping info about memory blocks. For example (untested, may have UB, but if you are writing cistom allocators you probably know how to do it right):

struct block {
   std::aligned_storage_t<sizeof(Bucket)> buck;
};

void* operator new (size_t size) {
   auto [bucket, slotAddress] = findFreeSlot(sizeof(block) + size);
   block* blk = new(slotAddress) block;
   Bucket* bck = new(&blk->buck) Bucket(bucket);
   return blk+1;
}

Now you can go from the address of MyObject to the bucket ID/pointer:

Bucket bucketOfMyObject (MyObject* m) {
   block* b = reinterpret_cast<block*>(m) - 1;
   return *reinterpret_cast<Bucket*>(b->buck);
}
n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243