Commit 7d4efeac authored by GILLES Sebastien's avatar GILLES Sebastien
Browse files

Typo.

parent f9a76c94
Pipeline #247546 passed with stage
in 9 minutes and 16 seconds
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# [Getting started in C++](./) - [Object programming](./0-main.ipynb) - [Member functions](./2-Member-functions.ipynb) # [Getting started in C++](./) - [Object programming](./0-main.ipynb) - [Member functions](./2-Member-functions.ipynb)
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
<h1>Table of contents<span class="tocSkip"></span></h1> <h1>Table of contents<span class="tocSkip"></span></h1>
<div class="toc"><ul class="toc-item"><li><span><a href="#Member-functions" data-toc-modified-id="Member-functions-1">Member functions</a></span></li><li><span><a href="#The-this-keyword" data-toc-modified-id="The-this-keyword-2">The <code>this</code> keyword</a></span></li><li><span><a href="#Separating-declaration-and-definition" data-toc-modified-id="Separating-declaration-and-definition-3">Separating declaration and definition</a></span></li><li><span><a href="#Const-methods" data-toc-modified-id="Const-methods-4">Const methods</a></span><ul class="toc-item"><li><span><a href="#mutable-keyword" data-toc-modified-id="mutable-keyword-4.1"><code>mutable</code> keyword</a></span></li></ul></li></ul></div> <div class="toc"><ul class="toc-item"><li><span><a href="#Member-functions" data-toc-modified-id="Member-functions-1">Member functions</a></span></li><li><span><a href="#The-this-keyword" data-toc-modified-id="The-this-keyword-2">The <code>this</code> keyword</a></span></li><li><span><a href="#Separating-declaration-and-definition" data-toc-modified-id="Separating-declaration-and-definition-3">Separating declaration and definition</a></span></li><li><span><a href="#Const-methods" data-toc-modified-id="Const-methods-4">Const methods</a></span><ul class="toc-item"><li><span><a href="#mutable-keyword" data-toc-modified-id="mutable-keyword-4.1"><code>mutable</code> keyword</a></span></li></ul></li></ul></div>
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Member functions ## Member functions
The struct we used previously would work the same in C code (with the exceptions of references: with a C compiler you would have to stick with pointers). The struct we used previously would work the same in C code (with the exceptions of references: with a C compiler you would have to stick with pointers).
But when Bjarne Stroustrup created the C++, its main idea was to extend these structs into full-fledged **classes** (to the point the work name of his language was *C with classes*...) But when Bjarne Stroustrup created the C++, its main idea was to extend these structs into full-fledged **classes** (to the point the work name of his language was *C with classes*...)
One of the idea that was missing with original C `struct` was the possibility to add as well member functions; this is no longer the case: One of the idea that was missing with original C `struct` was the possibility to add as well member functions; this is no longer the case:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <cmath> #include <cmath>
struct Vector struct Vector
{ {
double x; double x;
double y; double y;
double z; double z;
void init(double x, double y, double z) void init(double x, double y, double z)
{ {
this->x = x; this->x = x;
this->y = y; this->y = y;
this->z = z; this->z = z;
} }
double norm() double norm()
{ {
return std::sqrt(x * x + y * y + z * z); return std::sqrt(x * x + y * y + z * z);
} }
}; };
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
{ {
Vector v; Vector v;
v.init(5., 6., -4.2); v.init(5., 6., -4.2);
std::cout << v.norm() << std::endl; std::cout << v.norm() << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Let's do a bit of taxonomy here: Let's do a bit of taxonomy here:
- `init()` and `norm()` are called **member functions** or **methods**. The same remark concerning C++ purist I did for member variables may be applied here. - `init()` and `norm()` are called **member functions** or **methods**. The same remark concerning C++ purist I did for member variables may be applied here.
- **Method** is used in other programming languages, but for some reason Julia creators used this exact term for an entirely different concept. So to put in a nutshell a C++ method is akin to a Python one but not to what Julia calls a method. - **Method** is used in other programming languages, but for some reason Julia creators used this exact term for an entirely different concept. So to put in a nutshell a C++ method is akin to a Python one but not to what Julia calls a method.
- **Attributes** are in fact the data attributes AND the methods. It is however often used only to designate the data attributes. - **Attributes** are in fact the data attributes AND the methods. It is however often used only to designate the data attributes.
**WARNING**: In C++ you can't complete a class after the fact as you could for instance in Python. So all the methods and data atttributes have to be declared within the struct brackets here; if you need to add something you will have to recompile the class. This means especially you can't add directly a member function to a class provided by a third party library; we'll see shortly the mechanism you may use instead to do your bidding. **WARNING**: In C++ you can't complete a class after the fact as you could for instance in Python. So all the methods and data atttributes have to be declared within the struct brackets here; if you need to add something you will have to recompile the class. This means especially you can't add directly a member function to a class provided by a third party library; we'll see shortly the mechanism you may use instead to do your bidding.
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## The `this` keyword ## The `this` keyword
The `this->` may have puzzled you: it is a keyword to refer to the current object. So when you call `v.init(...)`, this is an implicit reference to `v`. The `this->` may have puzzled you: it is a keyword to refer to the current object. So when you call `v.init(...)`, this is an implicit reference to `v`.
In most cases, it might be altogether removed; we have to put it explicitly here solely because we named the `init` parameters with the same name as the data attribute. If not, we could have avoided to mention it completely. In most cases, it might be altogether removed; we have to put it explicitly here solely because we named the `init` parameters with the same name as the data attribute. If not, we could have avoided to mention it completely.
An usual convention is to suffix data attributes with a `_` (**be carefull**, attributes prefixed with a `_` is reserved by the C++ standard); doing so remove the need to the explicit `this`: An usual convention is to suffix data attributes with a `_` (**be careful**, attributes prefixed with a `_` is reserved by the C++ standard); doing so remove the need to the explicit `this`:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <cmath> #include <cmath>
struct Vector2 struct Vector2
{ {
double x_; double x_;
double y_; double y_;
double z_; double z_;
void init(double x, double y, double z) void init(double x, double y, double z)
{ {
x_ = x; x_ = x;
y_ = y; y_ = y;
z_ = z; z_ = z;
} }
double norm() double norm()
{ {
return std::sqrt(x_ * x_ + y_ * y_ + z_ * z_); return std::sqrt(x_ * x_ + y_ * y_ + z_ * z_);
} }
}; };
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
{ {
Vector2 v; Vector2 v;
v.init(5., 6., -4.2); v.init(5., 6., -4.2);
std::cout << v.norm() << std::endl; std::cout << v.norm() << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
That is not to say you should forget altogether the `this` keyword: it might be necessary in some contexts (for templates for instance - see [later](../4-Templates/3-Syntax.ipynb)...) That is not to say you should forget altogether the `this` keyword: it might be necessary in some contexts (for templates for instance - see [later](../4-Templates/3-Syntax.ipynb)...)
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Separating declaration and definition ## Separating declaration and definition
We have defined above the method directly in the class declaration; which is not very clean. It is acceptable for a very short method as here, but for a more complex class and method it is better to separate explicitly both. In this case you will have: We have defined above the method directly in the class declaration; which is not very clean. It is acceptable for a very short method as here, but for a more complex class and method it is better to separate explicitly both. In this case you will have:
- On one side, usually in a header file: - On one side, usually in a header file:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
struct Vector3 struct Vector3
{ {
double x_; double x_;
double y_; double y_;
double z_; double z_;
void init(double x, double y, double z); void init(double x, double y, double z);
double norm(); double norm();
}; };
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
- On another side the definition, usually in a source file which includes the header file: - On another side the definition, usually in a source file which includes the header file:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
void Vector3::init(double x, double y, double z) void Vector3::init(double x, double y, double z)
{ {
x_ = x; x_ = x;
y_ = y; y_ = y;
z_ = z; z_ = z;
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
double Vector3::norm() double Vector3::norm()
{ {
return std::sqrt(x_ * x_ + y_ * y_ + z_ * z_); return std::sqrt(x_ * x_ + y_ * y_ + z_ * z_);
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
{ {
Vector3 v; Vector3 v;
v.init(5., 6., -4.2); v.init(5., 6., -4.2);
std::cout << v.norm() << std::endl; std::cout << v.norm() << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Please notice the `::` syntax which specifies the class for which the implementation is provided. Also pay attention to the fact the `this` may as well be implicitly used. Please notice the `::` syntax which specifies the class for which the implementation is provided. Also pay attention to the fact the `this` may as well be implicitly used.
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Const methods ## Const methods
Are we happy here with what we have so far? Unfortunately, not quite... Are we happy here with what we have so far? Unfortunately, not quite...
If we define a simple free function that print the norm of a `Vector3`: If we define a simple free function that print the norm of a `Vector3`:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
void print_norm(const Vector3& v) void print_norm(const Vector3& v)
{ {
std::cout << v.norm() << std::endl; std::cout << v.norm() << std::endl;
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
... we see that doesn't compile. So what is happening? ... we see that doesn't compile. So what is happening?
The issue here is that the function `print_norm` takes as argument a constant reference, and has to guarantee the underlying object is not modified in the process. A "patch" would be to define it without the const: The issue here is that the function `print_norm` takes as argument a constant reference, and has to guarantee the underlying object is not modified in the process. A "patch" would be to define it without the const:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
void print_norm_no_const(Vector3& v) // BAD IDEA! void print_norm_no_const(Vector3& v) // BAD IDEA!
{ {
std::cout << v.norm() << std::endl; std::cout << v.norm() << std::endl;
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
{ {
Vector3 v; Vector3 v;
v.init(5., 6., -4.2); v.init(5., 6., -4.2);
print_norm_no_const(v); print_norm_no_const(v);
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Why is it such a poor idea? C++ is a compiled language, and this has its (many) pros and (many) cons. One of the advantages is to be able to leverage the compilation to detect at early time something is amiss. Here the compilation error is a good way to see we might be doing something wrong. Why is it such a poor idea? C++ is a compiled language, and this has its (many) pros and (many) cons. One of the advantages is to be able to leverage the compilation to detect at early time something is amiss. Here the compilation error is a good way to see we might be doing something wrong.
The sketchy "patch" I provided would be akin to ignoring the `const` feature almost entirely when objects are concerned. The sketchy "patch" I provided would be akin to ignoring the `const` feature almost entirely when objects are concerned.
The proper way is in fact quite the opposite: we may specify when writing a method that it is not allowed to modify the state of the object: The proper way is in fact quite the opposite: we may specify when writing a method that it is not allowed to modify the state of the object:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
struct Vector4 struct Vector4
{ {
double x_; double x_;
double y_; double y_;
double z_; double z_;
void init(double x, double y, double z); void init(double x, double y, double z);
double norm() const; // notice the additional keyword! double norm() const; // notice the additional keyword!
void dont_put_const_everywhere() const; void dont_put_const_everywhere() const;
}; };
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
void Vector4::init(double x, double y, double z) void Vector4::init(double x, double y, double z)
{ {
x_ = x; x_ = x;
y_ = y; y_ = y;
z_ = z; z_ = z;
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
double Vector4::norm() const double Vector4::norm() const
{ {
return std::sqrt(x_ * x_ + y_ * y_ + z_ * z_); return std::sqrt(x_ * x_ + y_ * y_ + z_ * z_);
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Please notice `const` needs to be specified both on declaration and on definition: if not provided in definition the signature of the method won't match and the compiler will yell. Please notice `const` needs to be specified both on declaration and on definition: if not provided in definition the signature of the method won't match and the compiler will yell.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
#include <iostream> #include <iostream>
void print_norm(const Vector4& v) void print_norm(const Vector4& v)
{ {
std::cout << v.norm() << std::endl; std::cout << v.norm() << std::endl;
} }
{ {
Vector4 v; Vector4 v;
v.init(5., 6., -4.2); v.init(5., 6., -4.2);
print_norm(v); print_norm(v);
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Obviously, if we try to ignore a `const` keyword, the compiler will also yell (it is and SHOULD BE very good at this!): Obviously, if we try to ignore a `const` keyword, the compiler will also yell (it is and SHOULD BE very good at this!):
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
void Vector4::dont_put_const_everywhere() const void Vector4::dont_put_const_everywhere() const
{ {
x_ = 0.; // ERROR! x_ = 0.; // ERROR!
} }
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### `mutable` keyword ### `mutable` keyword
Tread here with extreme caution here! Sometimes, you might want for a method to be mostly unable to modify the state of the class but you still need to modify one or more attribute. You may in this case use the `mutable` keyword when defining this attribute: Tread here with extreme caution here! Sometimes, you might want for a method to be mostly unable to modify the state of the class but you still need to modify one or more attribute. You may in this case use the `mutable` keyword when defining this attribute:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
struct Vector5 struct Vector5
{ {
double x_; double x_;
double y_; double y_;
double z_; double z_;
mutable unsigned int Nnorm_calls_; mutable unsigned int Nnorm_calls_;
void init(double x, double y, double z); void init(double x, double y, double z);
double norm() const; double norm() const;
}; };
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
void Vector5::init(double x, double y, double z) void Vector5::init(double x, double y, double z)
{ {
x_ = x; x_ = x;
y_ = y; y_ = y;
z_ = z; z_ = z;
Nnorm_calls_ = 0u; Nnorm_calls_ = 0u;
} }
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` C++17 ``` C++17
double Vector5::norm() const double Vector5::norm() const
{ {
++Nnorm_calls_; ++Nnorm_calls_;
return std::sqrt(x_ * x_ + y_ * y_ + z_ * z_); return std::sqrt(x_ * x_ + y_ * y_ + z_ * z_);