Why should I use simple arrays rather than container classes? - c++

What (if anything) can be done more efficiently with a array rather than a container?
I recently learned about C++ standard container classes. They have clear advantages and solve common problems with C-style arrays. The FAQs list on "why arrays are evil" can be summarized loosely like this:
1. subscripts are not checked
2. often it is required to allocate memory from the heap
3. not easy to insert elements in the middle
4. always passed as reference
I guess there are many cases, where one can live with these disadvantages. However, I am a bit puzzled about the question, what is it that can be done more efficient / easier with arrays rather than with containers? Or is there actually nothing like that and I really should not care about arrays anymore?

"However, I am a bit puzzled about the question, what is it that can be done more efficient / easier with arrays rather than with containers?"
Well, if you're referring to c-style arrays, with the current c++ standard, there's nothing left about the disadvantages of the classic standard c++ container classes (like e.g. std::vector) IMHO. These have a dependency to have allocatable memory (with new()), and that could well be be a restriction for your current (OS/bare metal) environment, as not being available out of the box.
The current standard provides std::array, that works without dynamic memory allocation needs, but fulfills all of the claims:
"1. subscripts are not checked"
std::array does subscript checking
"2. often it is required to allocate memory from the heap"
It's the client's choice where it's actually allocated with std::array. std::vector suffices anyway for doing this.
"3. not easy to insert elements in the middle"
Well, that's one point not supported well by std::array out of the box. But again, std::vector and other container classes support this well (as long your environment supports dynamic memory allocation)
"4. always passed as reference"
std::array supports passing by reference well, and much better than it can (can't) be achieved with c-style arrays.
Though there may be specialized cases for e.g. reusable object instance pools, or flyweight object instances, you might want to solve using the placement new() operator. Implementations for such solutions, usually would involve you're going to operate on raw c-style arrays.

Built-in arrays are a low-level tool with a somewhat awkward interface. They are needed to implement better to use classes, though: one of the nice things about C++ is that exposes many of the low-level tools to create efficient implementations of higher-level abstractions. To me, the primary uses of built-in arrays are these:
The mechanism to implement higher-level abstractions like std::vector<T> or std::array<T, N> (well, std::vector<...> and family don't really use built-in arrays but deal diretly with raw memory internally).
When I need an array of values initialized with a sequence of values allocated on stack, I'd use built-in arrays (std::array<...> can't deduce the number of arguments and anything using std::initializer_list<T> to get initialized won't have a fixed size).
Even though std::array<T, N> is really just a rewrite of [some of] the functionality of built-in arrays it has the nice feature that a debug implementation can assert assumptions which are made.
BTW, you list doesn't include on of the bigger issues: if you have a variable sized array, you'll need to give it your element type a default constructor. With C++11 the default constructor can, at least, be defaulted (that is an issue if your class needs another constructor) which can avoid initialization of objects about to be initialized. However, the various container classes take a lot of the complexity out of the picture.

Arrays on the stack can be more efficient than a vector, since the vector will always do a separate memory allocation. You're unlikely to notice the difference unless it's something performed many times in a large tight loop.

Or is there actually nothing like that and I really should not care about arrays anymore?
Consider that C++ dates to 1983, and it has seen a number of big changes since then. The container classes that are available now were designed to avoid the pitfalls that you listed, so it's not surprising that they're better in those respects. There is one thing that you can do with C-style arrays that you can't with modern container classes, however: you can compile your code with very old compilers. If you have a pressing need to maintain compatibility with compilers from the mid-1980's, you should stick with C-style arrays. Otherwise, use the newer, better approach.

c-stlye arrays have several advantages over stl containers (specifically std::array). Of course the same is true the other way around.
For one, with c-style arrays you have control over memory layout, which is extremely useful when interpreting network packets, or any similar data sources. This allows you to cast a block of memory to a struct, saving copy/assignment operations, which is necessary in some performance sensitive applications.
Another reason is simplicity - in a case where you don't need any of the benefits std containers offer, why use them?
And there's compatibility - different stl implementations will change with different compilers. Using arrays in the interfaces of shared libraries (so/dll) instead of stl containers allows the user to write against the shared library using almost any compiler. This is explicitly not true for stl containers.
Finally there's low level optimizations. There are situations where arrays can be faster than their stl equivalent std::array, although these situations are somewhat rare.

Related

Difference between a vector and a dynamically allocated array

What are the internal differences of choosing to use an std::vector vs a dynamically allocated array? I mean, not only performance differences like in this matching title question.
I mean, I try to design a library. So I want to offer a wrapper over an StackArray, that is just a C-Style array with some member methods that contains as a member T array[N]. No indirections and the new operator removed to force the implementor to have a array type always stored in the stack.
Now, I want to offer the dynamic variant. So, with a little effort, I just can declare something like:
template <typename T>
class DynArray
{
T* array,
size_t size,
size_t capacity
};
But... this seems pretty similar to a base approach to a C++ vector.
Also, an array stored in the heap can be resized by copying the elements to a new mem location (is this true?). That's pretty much the same that makes a vector when a push_back() operation exceeds its allocated capacity, for example, right?
Should I offer both API's if exists some notable differences? Or I am overcomplicated the design of the library and may I just have my StackArray and the Vector should be just the safe abstraction over a dynamically allocated array?
First there is a mindset (usually controversial) between the usage of the modern tools that the standard provides and the legacy ones.
You usually must be studing and asking things about C++ modern features, not comparing them with the old ones. But, for learning purposes, I have to admit that it's quite interesting dive deep somethimes in this topics.
With that in mind, std::vector is a collection that makes much more that just care about the bytes stored in it. There is a constraint really important, that the data must lie in contiguous memory, and std::vector ensures this in its internal implementation. Also, has an already well known, well tested implementation of the RAII pattern, with the correct usage of new[] and delete[] operators. You can reserve storage and emplace_abck() elements in a convenient and performant way which makes this collection really unique... there are really a lot of reasons that shows why std::vector is really different from a dynamically allocated array.
Not only is to worry about manual memory management, which almost an undesirable thing to do in modern C++ (embedded systems, or operating system themselves are a good point to discuse this last sentence). It's about to have a tool, std::vector<T> that makes your life as developer easier, specially in a prone-error language like C++.
Note: I say error-prone because it's a really hard to master language, which needs a lot of study and training. You can make almost everything in the world, and has an incredible amount of features that aren't begginer friendly. Also, the retrocompatibility constraint makes it really bigger, with literally thousand of things that you must care about. So, with a great power, always comes a great responsability.

<vector> push_back implementation internal

In std::vectors push_back implemetation, when size()==capacity() , the part, that it allocates two times more place and copy the elements of old , I want to ask, the most effective way of that copying?
This is not really specified in the standard, but left for the implementation. Thus, different C++ library versions will do this differently. However, the allocator of the vector must be used for any allocation and de-allocations
(hence no usage of realloc()).
What the standard does specify, though is that if objects can be moved (instead of copied) then they should be moved (since C++11). Typically, a new block of raw memory will be allocated, typically twice the size of the previous, and then the elements moved (or copied and destroyed) from the previous to the old. For the generic case, this moving/copying must be done one by one (since otherwise the integrity of the moves/copied cannot be guaranteed without knowledege of the internals of the respective move/copy constructors). Of course, for pod (=plain old data) types optimisations via std::memcpy() are possible (and most likely implemented).
You can try to look at your C++ library implementation, but be warned that the code can be quite opaque to those unfamiliar with meta template programming: the code is complicated because of the various optimisations (for pod, movable types, etc.).
Sounds a bit as if you wanted to implement std::vector<> yourself.
The most efficient way is to avoid reallocations in the first place, which often but not always is possible.
If you have prior knowledge about how many items you will push_back() next, you can use std::vector<>::reserve() and avoid reallocations altogether.
If you really were to implement yourself, you might want to use realloc(), in spite of the fact that this is a horrible function which can do - depending on parameter values anything from malloc() to free() to realloc().
The reason, why this function can be helpful is, that there is a good chance that there is still room behind the data block on the heap. realloc() is the only function which can avoid a 100% of the time moving of the data by expanding this heap blocks size, not touching the data at all, if there is enough room to do that.
The drawback of using realloc() is that your "special performance" implementation would forego the concept of allocators as well as fail to handle element types which require copy constructor calls etc. So you would best not name your vector vector but something like "SimpleDataVector" and possibly build in some mechanism to detect misuse.

Is it a bad idea to replace POD C-style array with std::valarray?

I'm working with a code base that is poorly written and has a lot of memory leaks.
It uses a lot of structs that contains raw pointers, which are mostly used as dynamic arrays.
Although the structs are often passed between functions, the allocation and deallocation of those pointers are placed at random places and cannot be easily tracked/reasoned/understood.
I changed some of them to classes and those pointers to be RAIIed by the classes themselves. They works well and don't look very ugly except that I banned copy-construct and copy-assignment of those classes simply because I don't want to spend time implementing them.
Now I'm thinking, am I re-inventing the wheel? Why don't I replace C-style array with std:array or std::valarray?
I would prefer std::valarray because it uses heap memory and RAIIed. And std::array is not (yet) available in my development environment.
Edit1: Another plus of std::array is that the majority of those dynamic arrays are POD (mostly int16_t, int32_t, and float) arrays, and the numeric API can possibility make life easier.
Is there anything that I need to be aware of before I start?
One I can think of is that there might not be an easy way to convert std::valarray or std::array back to C-style arrays, and part of our code does uses pointer arithmetic and need data to be presented as plain C-style arrays.
Anything else?
EDIT 2
I came across this question recently. A VERY BAD thing about std::valarray is that it's not safely copy-assignable until C++11.
As is quoted in that answer, in C++03 and earlier, it's UB if source and destination are of different sizes.
The standard replacement of C-style array would be std::vector. std::valarray is some "weird" math-vector for doing number-calculation-like stuff. It is not really designed to store an array of arbitrary objects.
That being said, using std::vector is most likely a very good idea. It would fix your leaks, use the heap, is resizable, has great exception-safety and so on.
It also guarantees that the data is stored in one contiguous block of memory. You can get a pointer to said block with the data() member function or, if you are pre-C++11, with &v[0] for a non-empty vector v. You can then do your pointer business with it as usual.
std::unique_ptr<int[]> is close to a drop-in replacement for an owning int*. It has the nice property that it will not implicitly copy itself, but it will implicitly move.
An operation that copies will generate compile time errors, instead of run time inefficiency.
It also has next to no run time overhead over that owning int* other than a null-check at destruction. It uses no more space than an int*.
std::vector<int> stores 3 pointers and implicitly copies (which can be expensive, and does not match your existing code behavior).
I would start with std::unique_ptr<int[]> as a first pass and get it working. I might transition some code over to std::vector<int> after I decide that intelligent buffer management is worth it.
Actually, as a first pass, I'd look for memcpy and memset and similar functions and make sure they aren't operating on the structures in question before I start adding RAII members.
A std::unique_ptr<int[]> means that the default created destructor for a struct will do the RAII cleanup for you without having to write any new code.
I would prefer std::vector as the replacement of c-style arrays. You can have a direct access to the underlying data (something like bare pointers) via .data():
Returns pointer to the underlying array serving as element storage.

Using multiple allocators efficiently

I have been researching switching my allocation method from simpling overloading new to using multiple allocators through the code base. However, how can I efficiently use multiple allocators? The only way I could devise through my research was having the allocators be globals. Although, this seemed to have issues since it is typically a "bad idea" to have the use of many globals.
I am looking to find out how to use multiple allocators efficiently. For example, I may have one allocator use only for a particular subsystem, and a different allocator for a different subsystem. I am not sure if the only way to do this is through using multiple global allocators, so I am hoping for a better insight and design.
In C++2003 the allocator model is broken and there isn't really a proper solution. For C++2011 the allocator model was fixed and you can have per instance allocators which are propagated down to contained objects (unless, of course, you choose to replace them). Generally, for this to be useful you probably want to use a dynamically polymorphic allocator type which the default std::allocator<T> is not required to be (and generally I would expect it not to be dynamically polymorphic although this may be the better implementation choice). However, [nearly] all classes in the standard C++ library which do memory allocation are templates which take the allocator type as template argument (e.g. the IOStreams are an exception but generally they don't allocate any interesting amount of memory to warrant adding allocator support).
In several of your comments you are insisting that allocators effectively need to be global: that is definitely not correct. Each allocator-aware type stores a copy of the allocator given (at least, if it has any instance level data; if it doesn't there isn't anything to store as is e.g. the case with the default allocator using operator new() and operator delete()). This effectively means that the allocation mechanism given to an object needs to stick around as long as there is any active allocator using it. This can be done using a global object but it can also be done using e.g. reference counting or associating the allocator with an object containing all objects to which it is given. For example, if each "document" (think XML, Excel, Pages, whatever structure file) passes an allocator to its members, the allocator can live as member of the document and get destroyed when the document is destroyed after all its content is destroyed. This part of the allocator model should work with pre-C++2011 classes, as long as they take an allocator argument, as well. However, in pre-C++2011 classes the allocator won't be passed to contained objects. For example, if you give an allocator to a std::vector<std::string> the C++2011 version will create the std::strings using the allocator given to the std::vector<std::string> appropriately converted to deal with std::strings. This won't happen with pre-C++2011 allocators.
To actually use allocators in a subsystem you will effectively need to pass them around, either explicitly as an argument to your functions and/or classes or implicitly by way of allocator-aware objects which serve as a context. For example, if you use any of the standard containers as [part of] the context passed around, you can obtain the used allocator using its get_allocator() method.
You can use new placement. This can be used either to specify a memory region, or to overload the type's static void* operator new(ARGS). Globals are not required, and really a bad idea here, if efficiency is important and your problems are demanding. You would need to hold on to one or more allocators, of course.
The best thing you can do is understand your problems and create strategies for your allocators based on the patterns in your program and on actual usage. The general purpose malloc is very good at what it does, so always use that as one baseline to measure against. If you don't know your usage patterns, your allocator will likely be slower than malloc.
Also keep in mind that these types you use will lose compatability with standard containers, unless you use a global or thread local and custom allocator for standard containers -- which quickly defeats the purpose in many contexts. The alternative is to also write your own allocators and containers.
Some uses for multiple allocators include reduced CPU usage, reduced fragmentation, and fewer cache misses. So the solution really depends on what type and where your allocation bottleneck is.
CPU usage will be improved by having lockless heaps for active threads, eliminating synchronization. This can be done in your memory allocator with thread local storage.
Fragmentation will be improved by having allocations with different lifespans be allocated from different heaps -- allocating background IO in a separate heap from the users active task will ensure the two do not confound one another. This is likely done by having a stack for your heaps, and push/popping when you're in different functional scopes.
Cache misses will be improved by keeping allocations within a system together. Having Quadtree/Octree allocations come from their own heap will guarantee there is locality in view frustrum queries. This is best done by overloading operator new and operator delete for the specific classes (OctreeNode).

C++ STL allocator vs operator new

According to C++ Primer 4th edition, page 755, there is a note saying:
Modern C++ programs ordinarily ought to use the allocator class
to allocate memory. It is safer and more flexible.
I don't quite understand this statement.
So far all the materials I read teach using new to allocate memory in C++.
An example of how vector class utilize allocator is shown in the book.
However, I cannot think of other scenarios.
Can anyone help to clarify this statement? and give me more examples?
When should I use allocator and when to use new? Thanks!
For general programming, yes you should use new and delete.
However, if you are writing a library, you should not!
I don't have your textbook, but I imagine it is discussing allocators in the context of writing library code.
Users of a library may want control over exactly what gets allocated from where. If all of the library's allocations went through new and delete, the user would have no way to have that fine-grained level of control.
All STL containers take an optional allocator template argument. The container will then use that allocator for its internal memory needs. By default, if you omit the allocator, it will use std::allocator which uses new and delete (specifically, ::operator new(size_t) and ::operator delete(void*)).
This way, the user of that container can control where memory gets allocated from if they desire.
Example of implementing a custom allocator for use with STL, and explanation: Improving Performance with Custom Pool Allocators for STL
Side Note: The STL approach to allocators is non-optimal in several ways. I recommend reading Towards a Better Allocator Model for a discussion of some of those issues.
Edit in 2019: The situation in C++ has improved since this answer was written. Stateful allocators are supported in C++11, and that support was improved in C++17. Some of the people involved in the "Towards a Better Allocator Model" were involved in those changes (eg: N2387), so that's nice (:
The two are not contradictory. Allocators are a PolicyPattern or StrategyPattern used by the STL libraries' container adapters to allocate chunks of memory for use with objects.
These allocators frequently optimize memory allocation by allowing
* ranges of elements to be allocated at once, and then initialized using a placement new
* items to be selected from secondary, specialized heaps depending on blocksize
One way or another, the end result will (almost always) be that the objects are allocated with new (placement or default)
Another vivid example would be how e.g. boost library implements smartpointers. Because smartpointers are very small (with little overhead) the allocation overhead might become a burden. It would make sense for the implementation to define a specialized allocator to do the allocations, so one may have efficient std::set<> of smartpointers, std::map<..., smartpointer> etc.
(Now I'm almost sure that boost actually optimizes storage for most smartpointers by avoiding any virtuals, therefore the vft, making the class a POD structure, with only the raw pointer as storage; some of the example will not apply. But then again, extrapolate to other kinds of smartpointer (refcounting smartpointers, pointers to member functions, pointers to member functions with instance reference etc. etc.))