简体   繁体   中英

Can I use an allocator object to free memory allocated by another allocator?

As far as I know, std::allocator is introduced by the library to allocate uninitialized unconstructed blocks of memory. So:

std::allocator<int> a;
auto ptr = a.allocate(100);
auto e = ptr;
while (e != ptr + 5)
    a.construct(e++, 0);

for (auto tmp = ptr; tmp != e; )
    std::cout << *tmp++ << ", ";
std::cout << std::endl;

std::allocator<int> a2;
std::allocator<int> a3 = a;

for (auto tmp = ptr; tmp != e; )
    a.destroy(tmp++);

//for (auto tmp = ptr; tmp != e; )
//  a2.destroy(tmp++); // is it UB using a2 here to destroy elements?

//for (auto tmp = ptr; tmp != e; )
//  a3.destroy(tmp++); // is it UB also?

a.deallocate(ptr, 100); // ok
//a2.deallocate(ptr, 100); // UB or OK?
//a3.deallocate(ptr, 100); // UB or ok?
  • What I am not sure about is whether using another allocator a2 , a3 objects (one of them) to free (deallocate) memory allocated by a is Undefined Behavior?

  • If it is OK why classes like std::vector has an allocator object and not just create a temporary one to alloc/dealloc memory?

Please clarify the questions above.

My reading of the standard says this is undefined behavior, unless the allocators compare equal. Table 34: Cpp17Allocator requirements [tab:cpp17.allocator] states for

a.deallocate(p,n)

that

Requires: p shall be a value returned by an earlier call to allocate that has not been invalidated by an intervening call to deallocate . n shall match the value passed to allocate to obtain this memory.

Where a is an lvalue of the allocator type. Since allocate is a member function I interpret the use of allocate in the quoted text to mean a.allocate which would mean you need the same or equal object to deallocate what was allocated.


This of course means that something like vector couldn't make a temporary when it needed and would need to keep the allocator as a member.

You can read about the specification for allocators here: https://en.cppreference.com/w/cpp/named_req/Allocator . They have changed a bit in different standard versions, but I will write about the C++17 and 20 renditions.

When you allocate memory with an allocator a1 , it can only be deallocated by a different allocator a2 if a1 == a2 .Quote from the standard :

a1 == a2
returns true only if storage allocated from each can be deallocated via the other.

std::allocator<T> is usually implemented as an empty type with no state. std::allocator<T>::is_always_equal::value is true , so all std::allocator<T> objects are equal. Thus, both deallocations are well defined.

If it was a stateful allocator, after AllocatorT a3 = a1 , a1 == a3 should be true , so you are safe to deallocate with it. AllocatorT a2; default constructs it, and a2 == a1 is probably not true, so you can't deallocate with a2 .

In C++17, you could also do AllocatorT a3 = std::move(a1); . This means that you could no longer deallocate with a1 (Unless a1 == a3 after the move), but only with a3 . This was changed in C++20, so you can only copy allocators.

Currently, most container implementations contain an object that inherits from the allocator so that if it is empty, empty base optimisation is used so that it doesn't take any extra bytes. So even if all objects are always the same, it doesn't hurt to use 0 extra bytes to store an allocator (And if it is stateful, an object needs to be stored anyway to deallocate). In C++20, this will probably be implemented with the [[no_unique_address]] attribute for the same effect.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM