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:
# [Getting started in C++](./) - [Object programming](./0-main.ipynb) - [Member functions](./2-Member-functions.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="#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:
## 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).
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:
%% Cell type:code id: tags:
``` C++17
#include <cmath>
struct Vector
{
double x;
double y;
double z;
void init(double x, double y, double z)
{
this->x = x;
this->y = y;
this->z = z;
}
double norm()
{
return std::sqrt(x * x + y * y + z * z);
}
};
```
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
Vector v;
v.init(5., 6., -4.2);
std::cout << v.norm() << std::endl;
}
```
%% Cell type:markdown id: tags:
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.
- **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.
**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:
## 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`.
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:
``` C++17
#include <cmath>
struct Vector2
{
double x_;
double y_;
double z_;
void init(double x, double y, double z)
{
x_ = x;
y_ = y;
z_ = z;
}
double norm()
{
return std::sqrt(x_ * x_ + y_ * y_ + z_ * z_);
}
};
```
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
Vector2 v;
v.init(5., 6., -4.2);
std::cout << v.norm() << std::endl;
}
```
%% 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)...)
%% Cell type:markdown id: tags:
## 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:
- On one side, usually in a header file:
%% Cell type:code id: tags:
``` C++17
struct Vector3
{
double x_;
double y_;
double z_;
void init(double x, double y, double z);
double norm();
};
```
%% Cell type:markdown id: tags:
- On another side the definition, usually in a source file which includes the header file:
%% Cell type:code id: tags:
``` C++17
void Vector3::init(double x, double y, double z)
{
x_ = x;
y_ = y;
z_ = z;
}
```
%% Cell type:code id: tags:
``` C++17
double Vector3::norm()
{
return std::sqrt(x_ * x_ + y_ * y_ + z_ * z_);
}
```
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
Vector3 v;
v.init(5., 6., -4.2);
std::cout << v.norm() << std::endl;
}
```
%% 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.
%% Cell type:markdown id: tags:
## Const methods
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`:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
void print_norm(const Vector3& v)
{
std::cout << v.norm() << std::endl;
}
```
%% Cell type:markdown id: tags:
... 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:
%% Cell type:code id: tags:
``` C++17
#include <iostream>
void print_norm_no_const(Vector3& v) // BAD IDEA!
{
std::cout << v.norm() << std::endl;
}
```
%% Cell type:code id: tags:
``` C++17
{
Vector3 v;
v.init(5., 6., -4.2);
print_norm_no_const(v);
}
```
%% 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.
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:
%% Cell type:code id: tags:
``` C++17
struct Vector4
{
double x_;
double y_;
double z_;
void init(double x, double y, double z);
double norm() const; // notice the additional keyword!
void dont_put_const_everywhere() const;
};
```
%% Cell type:code id: tags:
``` C++17
void Vector4::init(double x, double y, double z)
{
x_ = x;
y_ = y;
z_ = z;
}
```
%% Cell type:code id: tags:
``` C++17
double Vector4::norm() const
{
return std::sqrt(x_ * x_ + y_ * y_ + z_ * z_);
}
```
%% 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.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
void print_norm(const Vector4& v)
{
std::cout << v.norm() << std::endl;
}
{
Vector4 v;
v.init(5., 6., -4.2);
print_norm(v);
}
```
%% 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!):
%% Cell type:code id: tags:
``` C++17
void Vector4::dont_put_const_everywhere() const
{
x_ = 0.; // ERROR!
}
```
%% Cell type:markdown id: tags:
### `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:
%% Cell type:code id: tags:
``` C++17
struct Vector5
{
double x_;
double y_;
double z_;
mutable unsigned int Nnorm_calls_;
void init(double x, double y, double z);
double norm() const;
};
```
%% Cell type:code id: tags:
``` C++17
void Vector5::init(double x, double y, double z)
{
x_ = x;
y_ = y;
z_ = z;
Nnorm_calls_ = 0u;
}
```
%% Cell type:code id: tags:
``` C++17
double Vector5::norm() const
{
++Nnorm_calls_;
return std::sqrt(x_ * x_ + y_ * y_ + z_ * z_);
}
```
%% Cell type:code id: tags:
``` C++17
{
Vector5 v;
v.init(5., 6., -4.2);
for (int i = 0; i < 5; ++i)
v.norm();
std::cout << "Method 'norm()' was called " << v.Nnorm_calls_ << " times." << std::endl;
}
```
%% Cell type:markdown id: tags:
I must stress again that you should use this in a **last resort**!
For my part, I have used this in only two contexts:
* Using a work variable that would have been costly to reallocate at each call. This variable was always reset and used within the method that calls it and was not used to share a state between methods.
* For mutexes when using shared memory parallelism. This is way out of the scope of this tutorial.
%% 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)_
......
Markdown is supported
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