Undefined Behavior ?

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Bonjour,

voici un exemple venant de cplusplus.com:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// example: one class, two objects
#include <iostream>
using namespace std;

class Rectangle {
    int width, height;
  public:
    void set_values (int,int);
    int area () {return width*height;}
};

void Rectangle::set_values (int x, int y) {
  width = x;
  height = y;
}

int main () {
  Rectangle rect, rectb;
  rect.set_values (3,4);
  rectb.set_values (5,6);
  cout << "rect area: " << rect.area() << endl;
  cout << "rectb area: " << rectb.area() << endl;
  return 0;
}

On nous dit que sans appel à set_values() on a le droit à un UB parce que width et height ne sont pas défini (ce qui est vrai pour avoir testé :) ) Pourtant je pensais qu'il y avait alors un constructeur par défaut et que width et height se retrouvaient alors initialisés par défaut à 0. Où est l'erreur ?

+0 -0

Cette réponse a aidé l'auteur du sujet

Salut,

Un constructeur par défaut se contente d'appeler les "constructeurs par défaut" de chaque attribut de la classe. Cependant, pour les types natifs (bool, int, float, double, …), le "constructeur par défaut" (entre guillemets car je ne suis pas sûr que l'on puisse appeler cela comme ça pour les types natifs) ne définit pas de valeur. Ainsi :

1
int a;

a peut avoir n'importe quelle valeur de ce qu'il y avait à son emplacement mémoire.

Édité par victorlevasseur

+0 -0

Bonjour, Ton constructeur par défaut est le suivant :

1
2
3
4
5
6
7
8
9
class Rectangle {
public :
    Rectangle();
private :
    int width;
    int height;
};

Rectangle::Rectangle() : {}

Il ne fait rien, il se contente juste de créer l'objet sans initialiser aucun de ses attributs, seul un emplacement mémoire leur est réservé mais il est laissé tel qu'il était avant. Si tu veux initialiser tes variables avec une valeur par défaut, il faut que tu surcharges ton constructeur de la manière suivant :

1
2
3
4
5
6
7
8
9
class Rectangle {
public :
    Rectangle(int widthInit = 0, int heightInit = 0);
private :
    int width;
    int height;
};

Rectangle::Rectangle(int widthInit, int heightInit) : width(widthInit), height(heightInit) {}

Édité par Typhlos

+0 -0

Normalement le constructeur par défaut assigne les cases mémoires mais à aucun moment il ne donnent de valeur.

Si j'écris ce code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <string>


using std::cout;
using std::cerr;
using std::cin;
using std::endl;


int main()
{
    int i;
    i = 0;
    cout << i << endl;
    int j;
    cout << j << endl;

    return 0;
}

Et que je compile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# ludovic @ Tardis in /home/ludovic/workspace/C++/bordel [14:42:43] 
$ make
g++ -std=c++14 -W -Wall -Wextra -c main.cpp -o main.o
main.cpp: Dans la fonction ‘int main()’:
main.cpp:17:13: attention : ‘j’ is used uninitialized in this function [-Wuninitialized]
     cout << j << endl;
             ^
g++ -std=c++14 -W -Wall -Wextra main.o -o executable

# ludovic @ Tardis in /home/ludovic/workspace/C++/bordel [14:42:48] 
$ ./executable 
0
0

le compilateur me dit bien que j n'est pas initialisé.

Par contre le programme s’exécute quand même et ça je ne sais pas pourquoi.

Edit: je me suis fais devancer ;_;

Édité par LudoBike

« La Nature est un livre écrit en langage mathématique », Galilée

+0 -0

@LudoBike, Il s'exécute car le fait de ne pas initialiser une variable peut-être voulu par le développeur et donc le compilateur met juste un warning, il ne considère pas cela comme une erreur (bien que dans l'exemple que tu as donné cela en soit une), c'est au développeur de faire attention à ce que ses variables soit dans un état viable avant de les utiliser.

@Matthieuj, oui uniquement si tu as surchargé le constructeur comme je l'ai dis parce que le compilateur va faire appel au constructeur surchargé plutôt que au constructeur par défaut. Si tu ne surcharges pas le constructeur, les membres de ton objet ne seront pas initialisés.

Au passage on préfère initialisé un objet de cette manière :

1
2
Rectangle a{2, 3};
Rectangle b;  //si tu ne passes pas d'arguments au constructeur, il vaut mieux enlever les crochets

plutôt que de cette manière :

1
Rectangle c = Rectangle();

Édité par Typhlos

+1 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte