Standard Library Containers (STLs) support a nothrow allocation form?

The new operator (or for POD, malloc / calloc) supports a simple and efficient form of failure when allocating large blocks of memory.

Say we have this:

 const size_t sz = GetPotentiallyLargeBufferSize(); // 1M - 1000M T* p = new (nothrow) T[sz]; if(!p) { return sorry_not_enough_mem_would_you_like_to_try_again; } ... 

Is there such a construction for std :: containers or will I always have to handle the exception (expected !!) with std::vector and friends?




Maybe there is a way to write a user allocator that preallocates the memory and then passes that user allocator to the vector, so that until the vector asks for more memory than you put in the allocator in advance, it won't let you down?




A further thought: in fact, we needed a member function bool std::vector::reserve(std::nothrow) {...} in addition to the normal backup function. But since this would only make sense if the distributors were expanded to allow for fuzzy distribution, this simply will not happen. It seems that (something new) is good for something in the end :-)




Edit: As for why I even ask about this:

I thought about this question during debugging (1 random / second random debugger exception handling): If I set my debugger to 1st chance, I will catch any bad_alloc because I test low memory conditions, be annoying if it also caught those bad_alloc exceptions that are already well expected and handled in code. This was not / not a very big problem, but it just occurred to me that the sermon says that exceptions are for exceptional circumstances, and something that I already expect every odd call in the code to happen is not exceptional.

If new (nothrow) has legitimate uses, it will also have a vector-nothrow reserve.

+13
c ++ stl standard-library
Jan 28 2018-11-11T00:
source share
3 answers

By default, the standard STL container classes use the std::allocator class under the hood to allocate them, so they can throw std::bad_alloc if there is no memory available. It is interesting to note that the C ++ ISO specification on allocators states that the return value of any type of allocator should be a pointer to a block of memory capable of holding a certain number of elements, which automatically prevents the creation of a custom allocator that could potentially use nothrow new to have similar silent allocation failures. However, you could create a custom allocator that would terminate the program if the memory were not available, since then it is not up to date that the returned memory is valid when there is no memory left. :-)

In short, standard containers throw exceptions by default, and any way you can try to configure them using a special allocator to throw exceptions is out of specification.

+14
Jan 28 2018-11-11T00:
source share
— -

Too often we hear "I do not want to use exceptions because they are inefficient."

Unless you are referring to an “embedded” environment in which you want to disable all information about runtime type, you should not worry too much about the inefficiencies of exceptions if they are thrown appropriately. Running from memory is one of the following methods.

Part of the vector contract is that it will throw if it cannot highlight. If you write a custom allocator that returns NULL instead, this will be worse, as this will lead to undefined behavior.

If you need to use a dispenser that first tries to make a callback with failures, if one is available, and only if you still cannot allocate a throw, but still you should get an exception.

Can I give you a hint: if you really allocate such large amounts of data, then the vector is probably the wrong class, and you should use std :: deque instead. What for? Since deque does not require a continuous block of memory, but still a constant time search. And the benefits are doubled:

    • Allocations will occur less frequently. Because you do not need a continuous block, so you can have available memory, although not in one block.
    • Redistribution does not exist, but more so. Redistribution is expensive because it requires the movement of all objects. When you are in high volume mode, which can be very timely.

When I worked on such a system in the past, we found that we could store 4 times as much data using deque, as we could use the vector because of reason 1 above, and it was faster because of reason 2.

Something else we did was allocate a 2 MB backup buffer, and when we caught bad_alloc, we freed the buffer, and then still show that we reached capacity. But with the spare 2 MB, we at least knew that we had the memory to perform small operations to move data from memory to temporary disk storage.

Thus, we could sometimes catch bad_alloc and take the appropriate action, maintaining a consistent state, which is the purpose of exceptions, and not assume that running out of memory is always fatal and should never do anything but terminate the program (or even worse, cause undefined behavior) .

+5
Jan 28 2018-11-11T00:
source share

Standard containers use exceptions for this, you cannot get around it except trying to allocate only after you know that it will be successful. You cannot do this portable because the implementation is usually overly distributed over an indefinite amount. If you need to disable exceptions in the compiler, you are limited in what you can do with containers.

As for the “simple and efficient”, I think the std containers are quite simple and reasonably efficient:

 T* p = new (nothrow) T[sz]; if(!p) { return sorry_not_enough_mem_would_you_like_to_try_again; } ... more code that doesn't throw ... delete[] p; try { std::vector<T> p(sz); ... more code that doesn't throw ... } catch (std::bad_alloc) { return sorry_not_enough_mem_would_you_like_to_try_again; } 

The same number of lines of code. If this is a performance issue in the event of a crash, then your program should fail hundreds of thousands of times per second, in which case I doubt the development of the program a bit. I also wonder under what circumstances the cost of throwing and throwing an exception is significant compared to the cost of a system call, which new probably does to establish that it cannot satisfy the request.

But even better is how to write your own APIs to use exceptions:

 std::vector<T> p(sz); ... more code that doesn't throw ... 

Four lines are shorter than your source code, and the caller who currently needs to handle "sorry_not_enough_mem_would_you_like_to_try_again" can handle the exception. If this error code is transmitted across several levels of subscribers, you can save four lines at each level. C ++ has exceptions, and for almost all purposes, you can also accept this and write code accordingly.

Regarding "(expected!)" - sometimes you know how to handle the error condition. The thing is, you need to catch the exception. This is how exceptions should work. If the code that throws the exception somehow knew that there was nobody who ever caught it, then he could stop the program.

+2
Jan 28 '11 at 11:12
source share



All Articles