More Myths of My Blog Readers

Contents[Show]

Today, I conclude my story to your myths about C++. These myths are around function parameters, the initialisation of class members, and pointer versus references.

 dragon 4417431 1280

Always take the parameter by const reference (Gunter Königsmann)

You have two options when a function takes its parameter and doesn't want to modify it.

  • Take the parameter by value (copy it)
  • Take the parameter by const reference

This was the correctness perspective, but what can be said about the performance? The C++ core guidelines are specific about performance. Let's look at the following example.

void f1(const string& s);  // OK: pass by reference to const; always cheap

void f2(string s);         // bad: potentially expensive

void f3(int x);            // OK: Unbeatable

void f4(const int& x);     // bad: overhead on access in f4()

 

Presumably, based on experience, the guidelines state a rule of thumb:

  • You should take a parameter p by const reference if sizeof(p) > 4 * sizeof(int)
  • You should copy a parameter p if sizeof(p) < 3 * sizeof(int)

Okay, now you should know how big your data types are. The program sizeofArithmeticTypes.cpp gives the answers for arithmetic types.

// sizeofArithmeticTypes.cpp

#include <iostream>

int main(){
  
    std::cout << std::endl;
    
    std::cout << "sizeof(void*): " << sizeof(void*) << std::endl;  
    
    std::cout << std::endl;
 
    std::cout << "sizeof(5):  "  << sizeof(5)   << std::endl;
    std::cout << "sizeof(5l): "  << sizeof(5l)  << std::endl;
    std::cout << "sizeof(5ll): " << sizeof(5ll) << std::endl;
    
    std::cout << std::endl;
    
    std::cout << "sizeof(5.5f): " << sizeof(5.5f) << std::endl;
    std::cout << "sizeof(5.5): "  << sizeof(5.5)  << std::endl; 
    std::cout << "sizeof(5.5l): " << sizeof(5.5l) << std::endl;       
    
    std::cout << std::endl;
            
}

 

sizeof(void*) returns if it is a 32-bit or a 64-bit system. Thanks to an online compiler rextester, I can execute the program with GCC, Clang, and cl.exe (Windows). Here are the numbers for all 64-bit systems.

 

Rainer D 6 P2 540x540Modernes C++ Mentoring

Be part of my mentoring programs:

 

 

 

 

Do you want to stay informed about my mentoring programs: Subscribe via E-Mail.

GCC

sizeofGCC

Clang

sizeofClang

cl.exe (Windows)

sizeofVC

cl.exe behaves differently from GCC and Clang. A long int has only 4 bytes, and a long double has 8 bytes. On GCC and Clang, long int and long double have double size.

Deciding when to take the parameter by value or by const reference is just math. If you want to know the exact performance numbers for your architecture, there is only one answer: measure.

Initialization and Assignment in the Constructor are equivalent (Gunter Königsmann)

First, let me show you initialization and assignment in the constructor.

class Good{  
    int i;
public:
    Good(int i_): i{i_}{} 
};

class Bad{  
    int i;
public:
    Bad(int i_): { i = i_; } 
};

 

The class Good uses initialization but the class Bad assignment. The consequences are:

  • The variable i is directly initialized in the class Good
  • The variable i is default constructed and then assigned to the class Bad

 The constructor initialization is, on the one hand, slower but does not work on the other hand for const members, references, or members which can not be default-constructed possible.

// constructorAssignment.cpp

struct NoDefault{
    NoDefault(int){};
};

class Bad{
    const int constInt;
    int& refToInt;
    NoDefault noDefault;
public:
    Bad(int i, int& iRef){
        constInt = i;
        refToInt = iRef;
    }
    // Bad(int i, int& iRef): constInt(i), refToInt(iRef), noDefault{i} {}
};

int main(){
    
    int i = 10;
    int& j = i;
  
    Bad bad(i, j);
  
}

 

When I try to compile the program, I get three different errors.

  1. constInt is not initialized and can not be assigned in the constructor.
  2. refToInt is not initialized.
  3. The class NoDefault has no default constructor because I implemented one constructor for int. When implementing a constructor, the compiler will not automatically generate a default constructor.

 

constructorAssignment

In the second successful compilation, I used the second commented-out constructor, which uses initialization instead of assignment.

The example used references instead of raw pointers for a good reason.

You need Raw Pointers in your Code (Thargon110)

Motivated by a comment from Thargon110, I want to be dogmatic: NNN. What? I mean No Naked New. From an application perspective, there is no reason to use raw pointers. If you need a pointer like semantic, put your pointer into an smart pointer (You see: NNN), and you are done.

In essence, C++11 has a std::unique_ptr for exclusive ownership and a std::shared_ptr for shared ownership. Consequently, when you copy a std::shared_ptr, the reference counter is incremented, and when you delete the std::shared_ptr, the reference counter is decremented. Ownership means that the smart pointer keeps track of the underlying memory and releases the memory if it is not necessary anymore. The memory is not necessary any more in the case of the std::shared_ptr when the reference counter becomes 0.

So memory leaks are gone with modern C++. Now I hear your complaints. I'm happy to destroy them.

  • Cycles of std::shared_ptr can create a memory leak because the reference counter will not become 0. Right, put a std::weak_ptr in-between to break the cyclic reference: std::weak_ptr.
  • A std::shared_ptr has a management overhead and is, therefore, more expensive than a raw pointer. Right, use a std::unique_ptr.
  • A std::unique_ptr is not comfortable enough because it can't be copied. Right, but a std::unique_ptr can be moved.

 The last complaint is quite dominant. A small example should make my point:

 

// moveUniquePtr.cpp

#include <algorithm>
#include <iostream>
#include <memory>
#include <utility>
#include <vector>

void takeUniquePtr(std::unique_ptr<int> uniqPtr){          // (1)
    std::cout << "*uniqPtr: " << *uniqPtr << std::endl;
}

int main(){
  
    std::cout << std::endl;
  
    auto uniqPtr1 = std::make_unique<int>(2014);
    
    takeUniquePtr(std::move(uniqPtr1));                    // (1)
    
    auto uniqPtr2 = std::make_unique<int>(2017);
    auto uniqPtr3 = std::make_unique<int>(2020);
    auto uniqPtr4 = std::make_unique<int>(2023);
    
    std::vector<std::unique_ptr<int>> vecUniqPtr;
    vecUniqPtr.push_back(std::move(uniqPtr2));             // (2)
    vecUniqPtr.push_back(std::move(uniqPtr3));             // (2)
    vecUniqPtr.push_back(std::move(uniqPtr4));             // (2)
    
    std::cout << std::endl;
    
    std::for_each(vecUniqPtr.begin(), vecUniqPtr.end(),    // (3)
                  [](std::unique_ptr<int>& uniqPtr){ std::cout <<  *uniqPtr << std::endl; } );
    
    std::cout << std::endl;
    
}

 

The function takeUniquePtr in line (1) takes a std::unique_ptr by value. The critical observation is that you have to move the std::unique_ptr inside. The same argument holds for the std::vector<std::unique_ptr<int>> (line 2). std::vector as all containers of the standard template library want to own its elements, but to copy a std::unique_ptr is not possible. std::move solves this issue. You can apply an algorithm such as std::for_each on the std::vector<std::unique_ptr<int>> (line 3) if no copy semantic is used.

Use References instead of Raw Pointers

In the end, I want to refer to the critical concern of Thargon110. Admittedly, this rule is way more important in classical C++ without smart pointers because smart pointers are in contrast to raw pointers owners.

Use a reference instead of a pointer because a reference always has a value. Tedious checks such as the following one are gone with references. 

 

if(!ptr){
   std::cout << "Something went terrible wrong" << std::endl;
   return;
}
std::cout << "All fine" << std::endl;

 

Additionally, you can forget the check. References behave just as constant pointers.

What's next?

The C++ core guidelines define profiles. Profiles are a subset of rules. They exist for type safety, bounds safety, and lifetime safety. They will be my next topic.

 

 

 

Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, and Rob North.

 

Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, and Slavko Radman.

 

 

My special thanks to Embarcadero CBUIDER STUDIO FINAL ICONS 1024 Small

 

My special thanks to PVS-Studio PVC Logo

 

My special thanks to Tipi.build tipi.build logo

 

My special thanks to Take Up Code TakeUpCode 450 60

 

Seminars

I'm happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.

Bookable (Online)

German

Standard Seminars (English/German)

Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.

  • C++ - The Core Language
  • C++ - The Standard Library
  • C++ - Compact
  • C++11 and C++14
  • Concurrency with Modern C++
  • Design Pattern and Architectural Pattern with C++
  • Embedded Programming with Modern C++
  • Generic Programming (Templates) with C++

New

  • Clean Code with Modern C++
  • C++20

Contact Me

Modernes C++,

RainerGrimmDunkelBlauSmall

Stay Informed about my Mentoring

 

Mentoring

English 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)

Course: C++ Fundamentals for Professionals

Course: The All-in-One Guide to C++20

Course: Master Software Design Patterns and Architecture in C++

Subscribe to the newsletter (+ pdf bundle)

All tags

Blog archive

Source Code

Visitors

Today 2105

Yesterday 4344

Week 38983

Month 19229

All 12097438

Currently are 167 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments