Pass-by-value vs. pass-by-reference-to-const in C++

栏目: IT技术 · 发布时间: 4年前

内容简介:Comments about the

Pass-by-value vs pass-by-reference-to-const [C++]

Mar 26 ·6min read

Pass-by-value vs. pass-by-reference-to-const in C++

There is something wrong with this code. Take some time to think about it.

Pass-by-value vs. pass-by-reference-to-const in C++

The rule of thumb

As a C++ programmer, you might remember something like this from the past, when just starting your journey:

You: Sensei, I’m confused. What are those around the string ? Why is it int , but const string& ?

Sensei: bla bla bla, some drawings on the board, bla bla bla

You: Oh, I understand (sure you do), Sensei. So how do I know when to use which?

Sensei: That’s simple. All those built in types, like int/bool/char, should be by value, and all the others, the classes, should be by const-ref. And yes, string is not a built in type, it’s a class. There, even your Visual Studio shows them in different colors.

Or, maybe you didn’t have a Sensei, maybe it was just a Senpai. Maybe you are a self-taught programmer and StackOverflow was your Sensei. Anyway, you’ve come to a very simple rule, which is great to have, as a beginner. Simplicity helps in quick decision making, but it also gets stuck.

But it wasn’t over, there was still hope. Later you’ve (hopefully) read the “Effective C++: 55 Specific Ways to Improve Your Programs and Designs” by Scott Meyers. The “Item 20: Prefer pass-by-reference-to-const to pass-by-value.” is exactly about this. So you read it by nodding along, and here is the two-point summary of that item:

Pass-by-value vs. pass-by-reference-to-const in C++

I bet you stopped paying attention after the “ and STL ” and jumped to the next item.

I believe you’ve already figured out that the question we’re answering here is whether it’s correct to pass a std::shared_ptr by const-ref or not?

A Prototype for the machine, an Interface for you

A function’s prototype is defined to be the return type and the number and types of the input parameters. It’s basically what you see in the declaration.

This is a simplified version of a well-know std::getline function. This declaration contains the necessary information for the compiler. It needs to know the number of input parameters, their sizes and order to be able to generate the machine code for its calls. But it’s not just that, it also contains valuable information for humans, users of this function. Intuition suggests that str is a so-called out parameter , meaning we expect the result, the ‘line’ to be stored in that str . It would’ve been a different story if it was const std::string& str) . But that extra ‘const’ doesn’t make any difference in the machine code generated. In fact, your compiler doesn’t care about const-s. Well, it kind of does when doing a name lookup, but that’s just a check, it doesn’t affect the generated machine code anyway. Const correctness is a beautiful concept and quite unique to C++ (some other languages tried, but nothing). It serves as a contract between those who define the interface and those who use it.

To sum up, you define your interfaces such that the users will understand you, what your intentions are.

Back to our code

When one says, the code is wrong , it doesn’t necessarily mean there’s a bug. It might perform poorly or have a so-called code smell . This term is a vast generalization of constructs that are technically valid, execute the intention correctly (thus are not bugs), but still alarm about a deeper issue. And by the way, these smelly constructs vary from language to language, from paradigm to paradigm.

To understand what’s wrong in our code, let’s quickly recap what std::shared_ptr is. It’s a smart pointer that wraps the raw pointer along with a reference counter , providing a pointer-like interface. At any time the reference counter indicates how many shared_ptr objects are there co-owning this resource. It increments every time a co-owner is copied and decrements every time a co-owner is destroyed (out of scope, destructed). Eventually, when this counter comes to 0, the raw pointer is deleted, the resource is freed.

With this in mind, I’ll go ahead and write that function’s declaration again, thinking out loud:

void foo(... widget);

I know the type will have Widget somewhere somehow, but how exactly? Well, it depends. I ask myself the following questions:

Widget

There are 8 (2³) possible answer combinations to these questions, but because they are codependent, some of those combinations are irrelevant.

Pass-by-value vs. pass-by-reference-to-const in C++

Before moving on, I suggest an experiment. Go over this table from the other way around: look at the argument and try to answer the questions. Imagine, you're looking at a function with such an argument and try to figure out what was the intention behind.

So, not only the specific case dictates an interface, but also the latter explains a specific case. With this in mind, let’s finally get to our initial code.

Why don’t we have the case of the const std::shared_ptr<Widget>& in our table? Is it because we missed a scenario? Let’s find out by working our way form the other side, let’s try to understand what someone wants to tell us with such an interface.

Pass-by-value vs. pass-by-reference-to-const in C++

I understand this so:

  • I want a pointer to a Widget , but not a regular one, I want a smart one, a shared one more specifically.
  • As it’s not a shared_ptr<const Widget> , I might modify the original object, so beware.
  • Remember, when I said I want a shared pointer? Well, I’m not going to create a copy of it and become a co-owner. Basically, I’m not going to use the shared_ptr -ness of it, but if that object doesn't happen to be wrapped in a shared_ptr , you’re supposed to make it happen. If so, you’ll most probably cause a double deletion .

Not only this doesn’t make sense but it’s also a legit code smell. It entails uncertainty and the potential of new bugs.

Summary

Being experienced enough in C++ you’ve probably seen const-refs a lot, so much that you’d hardly take some time to question their correctness, and mostly you’re right to do so. What makes shared_ptr so special is its unique philosophy. But it’s not the only thing in the language and STL that has a philosophy. Take a new perspective on them. For example, question why the std::remove algorithm behaves the way it does and needs to be paired with erase ( erase-remove idiom ).

Pay more attention to the second point of the Item 20 and question it. It says “The rule doesn’t apply to … STL iterator and function types…”. Maybe there are some other cases too? After all, life has changed a lot since that book.

Martin Fowler once famously said: “ Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”. There is way more to the code than just instructions to be understood by the machine. Well designed constructs have a purpose, philosophy, and a clean, expressive interface. Those are important to understand for appropriate usage.

Comments about the weak_ptr in 3, 2, 1…


以上所述就是小编给大家介绍的《Pass-by-value vs. pass-by-reference-to-const in C++》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

程序员的数学2

程序员的数学2

平冈和幸、堀玄 / 陈筱烟 / 人民邮电出版社 / 2015-8-1 / CNY 79.00

本书沿袭《程序员的数学》平易近人的风格,用通俗的语言和具体的图表深入讲解程序员必须掌握的各类概率统计知识,例证丰富,讲解明晰,且提供了大量扩展内容,引导读者进一步深入学习。 本书涉及随机变量、贝叶斯公式、离散值和连续值的概率分布、协方差矩阵、多元正态分布、估计与检验理论、伪随机数以及概率论的各类应用,适合程序设计人员与数学爱好者阅读,也可作为高中或大学非数学专业学生的概率论入门读物。一起来看看 《程序员的数学2》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具