Commit 49877ddc authored by ROUVREAU Vincent's avatar ROUVREAU Vincent Committed by GILLES Sebastien
Browse files

Remove semicolon at the end of function declaration, and explains why it is...

Remove semicolon at the end of function declaration, and explains why it is required to add a semicolon at the end of a struct
parent c10a1de3
%% Cell type:markdown id: tags:
# [Getting started in C++](./) - [Object programming](./0-main.ipynb) - [Introduction to the concept of object](./1-Introduction.ipynb)
%% Cell type:markdown id: tags:
<h1>Table of contents<span class="tocSkip"></span></h1>
<div class="toc"><ul class="toc-item"><li><span><a href="#Motivation" data-toc-modified-id="Motivation-1">Motivation</a></span></li><li><span><a href="#The-C-response:-the-struct" data-toc-modified-id="The-C-response:-the-struct-2">The C response: the <code>struct</code></a></span></li><li><span><a href="#Passing-a-struct-to-a-function" data-toc-modified-id="Passing-a-struct-to-a-function-3">Passing a struct to a function</a></span><ul class="toc-item"><li><span><a href="#Pass-by-const-reference" data-toc-modified-id="Pass-by-const-reference-3.1">Pass-by-const-reference</a></span></li><li><span><a href="#Pass-by-pointer" data-toc-modified-id="Pass-by-pointer-3.2">Pass-by-pointer</a></span></li></ul></li><li><span><a href="#Initialization-of-objects" data-toc-modified-id="Initialization-of-objects-4">Initialization of objects</a></span></li></ul></div>
%% Cell type:markdown id: tags:
## Motivation
%% Cell type:markdown id: tags:
Sometimes, there are variables that are bound to be initialized and used together. Let's consider the position of a point in a three-dimensional space:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
#include <cmath> // For std::sqrt
double norm(double v_x, double v_y, double v_z)
{
return std::sqrt( v_x * v_x + v_y * v_y + v_z * v_z );
};
}
{
double v1_x, v1_y, v1_z;
v1_x = 1.;
v1_y = 5.;
v1_z = -2.;
std::cout << norm(v1_x, v1_y, v1_z) << std::endl;
double v2_x, v2_y, v2_z;
v2_x = 2.;
v2_y = 2.;
v2_z = 4.;
std::cout << norm(v2_x, v2_y, v2_z) << std::endl;
}
```
%% Cell type:markdown id: tags:
The code above is completely oblivious of the close relationship between `x`, `y` and `z`, and for instance the norm function takes three distinct arguments.
This is not just an inconveniency: this can lead to mistake if there is an error in the variables passed:
%% Cell type:code id: tags:
``` C++17
{
double v1_x, v1_y, v1_z;
v1_x = 1.;
v1_y = 5.;
v1_z = -2.;
double v2_x, v2_y, v2_z;
v2_x = 2.;
v2_y = 2.;
v2_z = 4.;
const double norm1 = norm(v1_x, v1_y, v2_z); // probably not what was intended, but the program
// has no way to figure out something is fishy!
}
```
%% Cell type:markdown id: tags:
## The C response: the `struct`
C introduced the `struct` to be able to group nicely data together and limit the risk I exposed above:
%% Cell type:code id: tags:
``` C++17
struct Vector
{
double x;
double y;
double z;
};
```
%% Cell type:code id: tags:
``` C++17
double norm(Vector v)
{
return std::sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}
{
Vector v1;
v1.x = 1.;
v1.y = 5.;
v1.z = -2.;
std::cout << norm(v1) << std::endl;
Vector v2;
v2.x = 2.;
v2.y = 2.;
v2.z = 4.;
std::cout << norm(v2) << std::endl;
}
```
%% Cell type:markdown id: tags:
Calling `norm` is now both more elegant (only one argument) and less dangerous (I can't mix by mistake coordinates from different objects).
Let's introduce at this point a bit of vocabulary:
- `x`, `y` and `z` in the structure are called **member variables** or **data attributes** (often shorten as **attributes** even if in a class this is actually not completely proper). On a side note: some C++ purists will be adamant only **member variables** should be used; but I rather use **data attributes** which is the term preferred in many others object programming languages.
- `Vector` is a **struct**, which is a somewhat simplified **class** (we will explain the difference when we'll introduce classes).
- `v1` and `v2` are **objects**.
Let's also highlight the `.` syntax which allows to access the attributes of an object (e.g `v1.x`).
%% Cell type:markdown id: tags:
### The semicolon at the end of a `struct`
This comes historically from the C, where a `struct` is considered as a type declaration and it allows to do:
**Xeus-cling issue:** Here cling doesn't manage to compile it but it is accepted rightfully by a full-fledged compiler (see for instance [@Coliru](http://coliru.stacked-crooked.com/a/3b77606ea8082485)):
%% Cell type:code id: tags:
``` C++17
struct Vector
{
double x;
double y;
double z;
} v1; // Here the struct is declared and at the same time an object v1 is created
v1.x = 1.;
v1.y = 5.;
v1.z = -2.;
```
%% Cell type:markdown id: tags:
This is the reason why a semicolon is always required at the end of a `struct` or a `class` declaration.
%% Cell type:markdown id: tags:
## Passing a struct to a function
In the `norm` function above, we passed as argument an object of `Vector` type by value. When we introduced functions, we saw there were three ways to pass an argument:
* By value.
* By reference.
* By pointers.
I didn't mention there the copy cost of a pass-by-value: copying a plain old data (POD) type such as an `int` or a `double` is actually cheap, and is recommended over a reference. But the story is not the same for an object: the cost of copying the object in the case of a pass-by-value may actually be quite high (imagine if there were an array with thousands of double values inside for instance) - and that's supposing the object is copyable (but we're not quite ready yet to deal with [that aspect](../3-Operators/4-CanonicalForm.ipynb#Uncopyable-class)).
### Pass-by-const-reference
So most of the time it is advised to pass arguments by reference, often along a `const` qualifier if the object is not to be modified by the function:
%% Cell type:code id: tags:
``` C++17
double norm_without_copy(const Vector& v)
{
return std::sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}
{
Vector v1;
v1.x = 1.;
v1.y = 5.;
v1.z = -2.;
std::cout << norm_without_copy(v1) << std::endl;
}
```
%% Cell type:markdown id: tags:
### Pass-by-pointer
Of course, if for some reason you prefer to use pointers it is also possible:
%% Cell type:code id: tags:
``` C++17
double norm(const Vector* const v) // can keep the name here: no possible ambiguity
{
return std::sqrt((*v).x * (*v).x + (*v).y * (*v).y + (*v).z * (*v).z);
}
{
Vector v1;
v1.x = 1.;
v1.y = 5.;
v1.z = -2.;
std::cout << norm(&v1) << std::endl;
}
```
%% Cell type:markdown id: tags:
This is more than little verbosy, so a shortcut has been introduced; `->` means you dereference a pointer and then calls the attribute:
%% Cell type:code id: tags:
``` C++17
double norm_with_pointer_shortcut(const Vector* const v)
{
return std::sqrt(v->x * v->x + v->y * v->y + v->z * v->z);
}
{
Vector v1;
v1.x = 1.;
v1.y = 5.;
v1.z = -2.;
std::cout << norm_with_pointer_shortcut(&v1) << std::endl;
}
```
%% Cell type:markdown id: tags:
## Initialization of objects
So far, we have improved the way the `norm` function is called, but the initialization of a vector is still a bit tedious. Let's wrap up a function to ease that:
%% Cell type:code id: tags:
``` C++17
void init(Vector& v, double x, double y, double z)
{
v.x = x;
v.y = y;
v.z = z;
}
```
%% Cell type:code id: tags:
``` C++17
{
Vector v1;
init(v1, 1., 5., -2.);
std::cout << "Norm = " << norm(v1) << std::endl;
}
```
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2021_
_This notebook is an adaptation of a lecture prepared by David Chamont (CNRS) under the terms of the licence [Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/)_
_The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment