C++ Core Guidelines: The Standard Library

Contents[Show]

The rules to the C++ standard library are mainly about containers, strings, and iostreams.

iso 154533 1280

Curiously, there is no section to the algorithms of the standard template library (STL) in this chapter. Curiously, because there is a proverb in the C++ community: If you write an explicit loop, you don't know the algorithms of the STL. Anyway. Only for completeness, let me start with the first three rules which provide not much beef.

SL.1: Use libraries wherever possible, because reinventing the wheel is a bad idea. Additionally, you benefit from the work of others. This means you use already tested and well-defined functionality. This holds, in particular, true, if you SL.2: Prefer the standard library to other libraries. Imagine, for example, you hire someone. The benefit is that he already knows the library and you don't have to teach him your libraries. You save a lot of money and time. I once had a customer who named his infrastructure namespace std. Of course, if you want to have a lot of fun, do it. If not: SL.3: Do not add non-standard entities to namespace std.

The next rules to STL containers are more concrete.

Containers

 The first rule is quite easy to argue.

SL.con.1: Prefer using STL array or vector instead of a C array

I assume you know a std::vector. One of the big advantages of a std::vector to a C arrays is it that the std::vector automatically manage its memory. Of course, that holds true for all further containers of the Standard Template Library. But now, let's have a closer look at the automatic memory management of std::vector.

std::vector

// vectorMemory.cpp

#include <iostream>
#include <string>
#include <vector>

template <typename T>
void showInfo(const T& t,const std::string& name){

  std::cout << name << " t.size(): " << t.size() << std::endl;
  std::cout << name << " t.capacity(): " << t.capacity() << std::endl;

}

int main(){
    
    std::cout << std::endl;

    std::vector<int> vec;                                            // (1)

    std::cout << "Maximal size: " << std::endl;
    std::cout << "vec.max_size(): " << vec.max_size() << std::endl;  // (2)
    std::cout << std::endl;

    std::cout << "Empty vector: " << std::endl;
    showInfo(vec, "Vector");
    std::cout << std::endl;

    std::cout << "Initialised with five values: " << std::endl;   
    vec = {1,2,3,4,5};
    showInfo(vec, "Vector");                                        // (3)
    std::cout << std::endl;

    std::cout << "Added four additional values: " << std::endl;
    vec.insert(vec.end(),{6,7,8,9});
    showInfo(vec,"Vector");                                         // (4)
    std::cout << std::endl;

    std::cout << "Resized to 30 values: " << std::endl;
    vec.resize(30);
    showInfo(vec,"Vector");                                         // (5)
    std::cout << std::endl;

    std::cout << "Reserved space for at least 1000 values: " << std::endl;
    vec.reserve(1000);
    showInfo(vec,"Vector");                                        // (6)
    std::cout << std::endl;

    std::cout << "Shrinke to the current size: " << std::endl;
    vec.shrink_to_fit();                                           // (7)
    showInfo(vec,"Vector");

}

 

To spare typing I wrote the small function showInfo. This function returns for a vector its size and its capacity. The size of a vector is its number of elements, the capacity of a container is the number of elements a vector can hold without an additional memory allocation. Therefore, the capacity of a vector has at least to be as big as its size. You can adjust the size of a vector with its method resize; you can adjust the capacity of a container with its method reserve.

But, back to the program from top to bottom. I create (line 1) an empty vector. Afterwards, the program displays (line 2) the maximum numbers of elements a vector can have. After each operation, I return their size and capacity. That holds for the initialization of the vector (line 3), for the addition of four new elements (line 4), the resizing of the containers to 30 elements (line 5) and the reserving of additional memory for at least 1000 elements (line 6). With C++11, you can shrink with the method shrink_to_fit (line 7) the vector's capacity to its size.

Before I present the output of the program on Linux let me make a few remarks.

  1. The adjustment of the size and the capacity of the container is done automatically. I haven't used any kind of memory operations like new and dele
  2. By using the method vec.resize(n) the vector vec will get new default-initialized elements, if n > cont.size() holds.
  3. By using the method vec.reserve(n) the container vec will get new memory for at least n elements, if n > cont.capacity() holds.
  4. The call shrink_to_fit is non-binding. That means the C++ runtime has not to adjust the capacity of a container to its size. But, my usages of the method shrink_to_fit with GCC, clang, or cl.exe always freed the unnecessary memory.

vectorMemory

Okay, but what is the difference between a C array and a C++ array?

std::array

std::array combines the best from two worlds. At one hand, std::array has the size and efficiency of a C-array; on the other hand, std::array has the interface of a std::vector. 

My small program compares the memory efficiency of a C array, a C++ array (std::array), and a std::vector. 

// sizeof.cpp

#include <iostream>
#include <array>
#include <vector>
 
int main(){
  
  std::cout << std::endl;
  
  std::cout << "sizeof(int)= " << sizeof(int) << std::endl;
  
  std::cout << std::endl;
  
  int cArr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  
  std::array<int, 10> cppArr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  
  std::vector<int> cppVec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  
  std::cout << "sizeof(cArr)= " << sizeof(cArr) << std::endl;            // (1)
  
  std::cout << "sizeof(cppArr)= " << sizeof(cppArr) << std::endl;        // (2)
  
                                                                         // (3)
  
  std::cout << "sizeof(cppVec) = "   << sizeof(cppVec) + sizeof(int) * cppVec.capacity() << std::endl;
  std::cout << "               = sizeof(cppVec): " << sizeof(cppVec) << std::endl;
  std::cout << "               + sizeof(int)* cppVec.capacity(): "   << sizeof(int)* cppVec.capacity() << std::endl;

  std::cout << std::endl;
  
}

 

sizeof

Both, the C array (line1) and the C++ array (line 2) take 40 bytes. That is exactly sizeof(int) * 10. In contrast, the std::vector needs additional 24 bytes (line 3) to manage its data on the heap.

This was the C part of a std::array but the std::array supports the interface of a std::vector. This means, in particular, that std::array knows its size and, therefore, error-prone interfaces such as the following one are a heavy code-smell.

 

void bad(int* p, int count){
   ... 
}

int myArray[100] = {0};    
bad(myArray, 100);

// ----------------------------- 

void good(std::array<int, 10> arr){
   ...
}

std::array<int, 100> myArray = {0};
good(myArray);

 

When you use a C array as a function argument, you remove almost all type information and pass it as a pointer to its first argument. This is extremely error-prone because you have to provide the number of elements additionally. This will not hold if your function accepts an std::array<int, 100>.

If the function good is not generic enough, you can use a template.

template <typename T>
void foo(T& arr){

   arr.size();                                         // (1)

}


std::array<int, 100> arr{};                                             
foo(arr);                                                    
    
std::array<double, 20> arr2{};
foo(arr2);  

 

Because a std::array knows its size, you can ask for it such in line 1.

What's next?

The next two rules to containers are quite interesting. In the next post, I give an answer to the question: When to use which container?

 

Thanks a lot to my Patreon Supporters: Paul Baxter,  Meeting C++, Matt Braun, Avi Lachmish, Roman Postanciuc, Venkata Ramesh Gudpati, Tobias Zindl, Marko, Ramesh Jangama, G Prvulovic, Reiner Eiteljörge, Benjamin Huth, Reinhold Dröge, Timo, Abernitzke, Richard Ohnemus, Frank Grimm, and Sakib.

Thanks in particular to:  TakeUpCode 450 60     crp4

 

Get your e-book at Leanpub:

The C++ Standard Library

 

Concurrency With Modern C++

 

Get Both as one Bundle

cover   ConcurrencyCoverFrame   bundle
With C++11, C++14, and C++17 we got a lot of new C++ libraries. In addition, the existing ones are greatly improved. The key idea of my book is to give you the necessary information to the current C++ libraries in about 200 pages.  

C++11 is the first C++ standard that deals with concurrency. The story goes on with C++17 and will continue with C++20.

I'll give you a detailed insight in the current and the upcoming concurrency in C++. This insight includes the theory and a lot of practice with more the 100 source files.

 

Get my books "The C++ Standard Library" (including C++17) and "Concurrency with Modern C++" in a bundle.

In sum, you get more than 600 pages full of modern C++ and more than 100 source files presenting concurrency in practice.

 

Get your interactive course

 

Modern C++ Concurrency in Practice

C++ Standard Library including C++14 & C++17

educative CLibrary

Based on my book "Concurrency with Modern C++" educative.io created an interactive course.

What's Inside?

  • 140 lessons
  • 110 code playgrounds => Runs in the browser
  • 78 code snippets
  • 55 illustrations

Based on my book "The C++ Standard Library" educative.io created an interactive course.

What's Inside?

  • 149 lessons
  • 111 code playgrounds => Runs in the browser
  • 164 code snippets
  • 25 illustrations
 

Cranepay

 

Add comment


My Newest E-Books

Course: Modern C++ Concurrency in Practice

Course: C++ Standard Library including C++14 & C++17

Course: Embedded Programming with Modern C++

Course: Generic Programming (Templates)

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 2158

All 2689691

Currently are 225 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments