Skip to main content

C++ Containers

There are a lot of different containers in C++. A container is a holder object that stores a collection of other objects (its elements). They are implemented as class templates, which makes them able to store almost any type of objects.

The container manages the storage space for its elements and provides member functions to access them, either directly or through iterators. This makes them much more powerful and safe than the plain old C-arrays.

Containers implements structures very commonly used in data science: dynamic arrays (vector), queues (queue), stacks (stack), heaps (priority_queue), linked lists (list), trees (set), associative arrays (map) and more. Each of these have their own characteristics.

C++ have a lot of containers, but in this series of articles we will first look at the characteristics of each of the four ones you probably will need in your toolbox:
  • std::list 
  • std::vector 
  • std:set 
  • std::map 

Today we will start with the list and later issues will handle the rest. We also will look at iterators.

The list


std::list is implemented as a double-inked list. A list allow insert and erase operations anywhere within the sequence, and iteration in both directions. The cost of growing the list is always constant time and memory.

The drawback with linked lists are that they add extra data for each element so the total storage will be larger than for a C-array or std::vector. So if you have long lists, they might be better stored as vectors to conserve memory. Another drawback is that lists don’t allow direct access to their elements, you will have to iterate through the container to find a specific element.

In the sample below you can see some of the features of std::list. We create a list of numbers, push numbers at front and back.

Then we use an iterator to point to an element in the list. The iterator is set by the find-algorithm. We will get back to the algorithms in STL (standard template library) in a later issue of this news-letter.

int main()
{
    // Create a list containing integers    
    std::list<int> l = { 7, 5, 16, 8 };
    // Add the integer 25 to the front of the list    
    l.push_front(25);
    // Add the integer 13 to the back of the list    
    l.push_back(13);

    // Insert 42 before 16 by searching    
    auto it = std::find(l.begin(), l.end(), 16);
    if (it != l.end()) {
        l.insert(it, 42);
    }
    // Iterate and print values of the list    
    for (auto element: l) {
        std::cout << element << std::endl;
    }
}

Iterators

A std::iterator is an object that, pointing to some element in a container). It also has the ability to iterate through the elements of that range using a standard set of operators for example increment (++) and dereference (*) operators. The syntax for the std::iterators in many ways works as standard c-pointers but with a lot more under the hood.

To iterate over a container with an iterator has two benefits: It is generic and works on all containers so if you change container type you don’t have to change your code. It is also guaranteed to be the most efficient form of iteration for the given container.

When should you use the C++ std::list?


The std::list is ideal for small datasets that change a lot, especially when you add things in other places than at the end. For larger datasets the memory-overhead could be too large to ignore. This is always compared to the size of the objects stored: Big objects might be expensive to move, which might be needed in other containers.

Comments

Popular posts from this blog

Balancing Present Needs and Future Growth

In software development, traditional project planning often emphasizes immediate needs and short-term goals. However, Bentoism, which stands for "Beyond Near-Term Orientation," provides a multidimensional framework that can improve software project planning. It advocates for a balance between short-term achievements and long-term sustainability, considering both individual and collective impacts. Technical debt and architectural debt are inevitable challenges that teams must navigate. If managed properly, these debts can help long-term sustainability and growth. Bentoism, with its forward-looking and holistic perspective, offers a nuanced framework for handling these challenges while promoting continuous improvement.  Understanding Bentoism  Bentoism, inspired by the structure of a bento box that contains a variety of foods in separate compartments, encourages a broader perspective in decision-making. It promotes consideration of 'Now Me' (current self-interests), ...

Digital Dialectics: A Marxist Exploration of Technology and Class in the Software Industry

In this blog series, we discussed various aspects of programming and technology from a Marxist perspective. Here's a summary: Marxist Analysis of Programming and Technology: We explored several critical aspects of Marxist theory applied to programming and technology, including the means of production in software development, class struggle and labour relations, the commodification of software, alienation in the tech industry, and the digital divide and technological inequality. Dialectical Materialism and Base and Superstructure: We delved into applying Marx's dialectical materialism to technology development, analyzing how technological advancements lead to societal changes. We also discussed the base and superstructure model in the context of the digital age, focusing on the technical infrastructure and the evolving social and cultural norms. Class Struggle in the Software Industry: We examined the dynamics between different groups in the tech industry, including tech compa...

Software Projects as an Orchard

This blog is named The Sourcerers Orchard. The title is intended as a pun about source code and the orchard as an analogy between software development and handling an orchard. Creating a new orchard is an endeavour that blends the art of gardening with science. The same could be true for software development. We often talk about software as an industry, and this mindset harms our work. We are not an industry; we do not repetitively produce the same unit at an assembly line. We grow new things in a partly unpredictable world. Systems like SAFe are born in industrial thinking, like modern mercantilism, focused on numbers, not growth. We need a new way of thinking, to make high quality software instead of failing production lines. Planning Your Orchard Embarking on creating a new software project is akin to cultivating a thriving orchard from the ground up. It’s a journey that requires patience, dedication, and a strategic approach to nurturing growth and overcoming challenges. Let’s expl...