Probleme creation classe

Fermé
clem - 10 juin 2017 à 22:00
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 - 15 juin 2017 à 10:02
Bonjour,
Je me lance dans l'apprentissage de cpp après Python mais je bute pas mal... Par exemple, je tente ici se créer une simple classe avec une seule méthode mais m_rect ne veut pas prendre en valeur mon array rect...
#include <iostream>
using namespace std;

class Rect 
{
    public:
    Rect(int rect[4]): 
    m_rect(rect)
    {}
    bool collidepoint(int ptx, int pty) { 
        for (int x= m_rect[0]; x <= m_rect[2]; x++) {
        if (x == ptx) {
            for (int y= m_rect[1]; y <= m_rect[3]; y++) {
                if (y == pty) {
                    return true;
                }
            }
        } 
    }
    return false;
    }
    private:
    int m_rect[4];
};

int main() {
    int rectangle[4] = {0, 0, 20, 20};
    Rect sprite(rectangle);
    cout << sprite.collidepoint(5, 2);
    return 0;
}

Merci d'avoir pris le temps de lire !

A voir également:

4 réponses

mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749
Modifié le 12 juin 2017 à 10:12
Bonjour,

Comme le dit probablement ton compilateur :

(mando@velvet) (~) $ g++ plop.cpp 
plop.cpp: In constructor ‘Rect::Rect(int*)’:
plop.cpp:8:15: error: incompatible types in assignment of ‘int*’ to ‘int [4]’
m_rect(rect)


Personnellement je déclarerais le bloc privé en début de classe (j'aime bien savoir ce que contient une classe avant de voir ses méthodes), même ce n'est pas obligatoire.

Tu peux utiliser un
std::vector<int>
à la place.

Vu que collidepoint ne modifie pas l'objet, tu peux rajouter le mot clé
const
devant l'accolade, c'est plus propre. Enfin en C les méthodes par conventions nommés sous l'une de ces deux formes
ma_methode
ou
maMethode
.

Tu as oublié le retour à la ligne (
std::endl
)

Enfin le
using namespace std;
est à mon avis au début une mauvaise habitude. En fait tant que tu es dans un .cpp pas de problème, par contre le jour où tu écris un header (.hpp) il ne faut pas en utiliser.

#include <iostream>
#include <vector>
#include <cassert>

class Rect
{
    private:

        std::vector<int> m_rect;

    public:

        Rect(const std::vector<int> & rect):
            m_rect(rect)
        {
            assert(rect.size() == 4);
        }

        bool collide_point(int ptx, int pty) {
            for (int x= m_rect[0]; x <= m_rect[2]; x++) const {
                if (x == ptx) {
                    for (int y= m_rect[1]; y <= m_rect[3]; y++) {
                        if (y == pty) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }
};

int main() {
    std::vector<int> rectangle = {0, 0, 20, 20};
    Rect sprite(rectangle);
    std::cout << sprite.collide_point(5, 2) << std::endl;
    return 0;
}


Note que l'initialisation du vecteur à pu se faire comme un tableau. Toutefois, cette syntaxe peut ne pas être prise si tu n'as pas un compilateur récent. Dans ce cas, tu peux initialiser les cases une à une :

std::vector<int> rectangle(4);
rectangle[0] = 0;
rectangle[1] = 0;
rectangle[2] = 20;
rectangle[3] = 20;


Bonne chance
0
Merci beaucoup!!
Est-ce-que cassert fonctionne comme une assertion python ? (renvoie assertion error) Et pourquoi ne pas utiliser des tableaux statiques ? Juste pour obtenir la taille (je crois qu'on est obligé de passer par un for pour les statiques non? ) ?
Désolé de te poser autant de questions :p :$
Ps - je code sur mobile alors j'ai que un .cpp
0
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749
Modifié le 13 juin 2017 à 10:02
Est-ce-que cassert fonctionne comme une assertion python ?

Les assertions fonctionnent comme en python, elles provoquent une erreur si le test n'est pas vérifié.

Il y a toutefois quelques différences. En python, tu peux préciser le message d'erreur, et tu peux rattraper avec un
try ... except
l'exception levée par l'assertion. Ce n'est pas le cas avec
assert
en C/C++, car cette fonction vient du C, donc elle n'a ni paramètre optionnel (le message d'erreur), et ne lève pas d'exception (notion qui n'existe pas en C), elle provoque juste un arrêt abrupt du programme.

Chose intéressante par contre, passer l'option
-DNDEBUG
à la compilation permet de compiler le programme comme si tous les assertions étaient commentés. C'est une des raisons pour lesquelles un assert en C ne doit jamais contenir du code qui modifie une variable, ni de tests qui ont du sens dans une exécution "normale" (par exemple tester si une zone mémoire a été bien allouée). En C/C++ les assertions doivent plus être vus comme des filets de sécurité pour le programmeur et qui sont sensées être tout le temps vérifiées.

Et pourquoi ne pas utiliser des tableaux statiques ?

Tu peux mais dans ce cas il tu n'as pas d'opérateur
=
. Un tableau statique n'est pas la meilleure manière de comprendre ce qui se passe. Quand tu écris :

int tab[4] = {1,2,3,4}


... en vrai tu dis :
- crées un bloc statique de 4 entiers
- initialise le avec les valeurs 1;2.3.4
- mémorise l'adresse de ce bloc de 4 entiers dans
tab

- donc
tab
est une adresse (un pointeur) de type
int *
, qui vaut mettons
0x00123ab
.

Ainsi si tu écris ensuite
int * p = tab
, tu recopies l'adresse
0x00123ab
dans p. Ce qui signifie que tu n'as pas recopié le bloc de 4 entiers. Si ensuite tu évalues ce qui est à cette adresse avec l'opérateur
[]
ou l'opérateur
*
tu vas donc examiner la même zone mémoire. Dans l'algèbre des pointeurs
*(tab +i)
équivaut à
tab[i]
. Le type du pointeur, ici
int *
, permet de correctement donner un sens à ces opérateurs (i.e. se placer au bon endroit et lire le bon nombre d'octets).

En conséquence, si ensuite tu fais
p[2] = 7;
,
tab[2]
est aussi modifié (puisqu'il s'agit de la même zone.

Du coup, pour faire une recopie d'un tableau statique, il faudrait passer par une fonction du genre
memcpy
, ou, moins rapide mais équivalent, recopier case par case le premier tableau dans le second.

#include <cstring>
#include <iostream>

int main() {
    int tab[4] = {1,2,3,4};
    int p[4];
    memcpy(p, tab, 4 * sizeof(int));
    for (int x : tab) std::cout << x << std::endl;
    for (int x : p)   std::cout << x << std::endl;
    return 0;
}


Juste pour obtenir la taille (je crois qu'on est obligé de passer par un for pour les statiques non? ) ?

Comme le montre le code ci-dessus, la syntaxe des
:
permet de s'en sortir sans la taille. Si cette syntaxe n'est pas supportée (typiquement sur les compilateurs un peu anciens), tu peux toujours écrire une boucle
for(unsigned i = 0; i < 4; ++i) { ... }
.

Le seul intérêt du
std::vector<int>
est ici d'avoir accès à une syntaxe plus dans l'esprit de python, même si cela induit un léger surcoût par un rapport un tableau statique.

Bonne chance
0
haaa merci ! Encore un peu compliquer pour moi mais je comprend mieux.
0
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749
14 juin 2017 à 09:29
Je pense que la notion la plus "nouvelle" par rapport au python est vraiment la notion de pointeur (= adresse). Tout objet a une adresse. Par contre selon son type il occupe plus ou moins de taille en mémoire. C'est ce qui conduit à typer les adresses, afin de caractériser la zone pointée. Ensuite c'est juste une gymnastique à prendre entre l'adresse et ce qui se trouve à cette adresse.

Est-ce que ton problème est résolu ?
0
Oui merci bezucoup encore une fois. Entre temps j'ai divisé la classe (.h et .cpp), j'ai rajouté par exemple colliderect pour tester si deux rectangle se touchent et jai une fonction operator<< qui marche. Maintenant je dois vraiment apprendre comment utiliser adresse et pointeur et faire attention à ne pas confondre adresse et référence. J'ai bien compris les principes mais c'est en pratique où je bloque un peu. À part ca je trouve ça très dommage qu'il n'y ai pas moyen de try except ou de lever une exeption. Dans ma tête je me dis qu'il suffit de créer cette fonction dans un genre de module / biblio mais je n'ai pas vraiment de vision générale d'un langage de programation. En gros comment ça fonctionne vraiment, le derrière des choses. Si les limites sont fixées par l'os, comment, comment est fait un os, qu'en est-t-il de lz sécurité... Bref, beaucoup de choses qui me prendront bc de temps.
0
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749
14 juin 2017 à 22:50
Oui merci beaucoup encore une fois. Entre temps j'ai divisé la classe (.h et .cpp).

Bien ! Du moment qu'une méthode n'est pas
template
elle est sensée être effectivement implémentée dans le fichier .cpp ou être précédée du mot clé
inline
.

J'ai rajouté par exemple colliderect pour tester si deux rectangle se touchent et jai une fonction operator<< qui marche.

Si cet opérateur est bien écrit il est de la forme :

#include <ostream>

std::ostream & operator << (std::ostream & os, const Rect & r) {
  os << ... ;
  return os;
}


Maintenant je dois vraiment apprendre comment utiliser adresse et pointeur et faire attention à ne pas confondre adresse et référence.

Conceptuellement les deux notions sont proches. La principale différence est qu'une référence est initialisée sur un objet instancié. Un pointeur peut ne pas être initialisé, ou être initialisé à
NULL
.

Je t'invite à lire cette explication :
https://forums.commentcamarche.net/forum/affich-27614885-c-est-quoi-la-difference-entre-passage-par-reference-et-passage#1

J'ai bien compris les principes mais c'est en pratique où je bloque un peu.

Si tu peux être plus précis je pourrai peut-être t'éclairer.

À part ca je trouve ça très dommage qu'il n'y ai pas moyen de try except ou de lever une exeption.

On s'est mal compris. En C/C++ une assertion ne lève pas d'exception. En C la notion d'exception n'existe pas, mais elle existe en C++. Tu peux définir des héritages entre tes classes d'exception et les rattraper différemment une exception selon son type, exactement comme en python.

#include <stdexcept>

int main() {
  try {
    raise std::runtime_error("plop");
  } catch(MonException & e) {
    // ...
  } catch(...){ // Rattrape toutes les exceptions non rattrapées
    // ...
  }
  return 0;
}


Dans ma tête je me dis qu'il suffit de créer cette fonction dans un genre de module / biblio mais je n'ai pas vraiment de vision générale d'un langage de programmation.

Oui tu pourrais tout à fait définir une assertion qui lève une exception :

class MonException {};

inline MonException mon_assert(bool b) {
#ifndef DNDEBUG
  if (!b) throw MonException();
#endif
}


En gros comment ça fonctionne vraiment, le derrière des choses. Si les limites sont fixées par l'os, comment, comment est fait un os, qu'en est-t-il de la sécurité...

Un OS c'est un vaste sujet :) Mais peut être que plonger dans linux t'éclairerait, l'avantage étant qu'une grande partie est codée en C et est open source.

Bref, beaucoup de choses qui me prendront bc de temps.

Oui :-)
0
Ah! super stdexcept alors. Oui merci je lirais l'explication demain (j'ai mon bac cet aprem :$). D'ailleurs j'ai voulu remplacer mes arrays statiques par des vecteurs, mais ça ne fonctionne pas... La console me dit seulement No Ouput alors je suis pas très éclairé... Pour ne pas copier 80lignes de codes je met juste le header de rect et le main.
#include <iostream>
#include <vector>
using namespace std;

// Rect.h
class Rect
{
public:
Rect(int, int, int, int);
~Rect();
friend std::ostream& operator<<(std::ostream&, Rect const&)
;
bool collidepoint(int, int) const;
bool colliderect(Rect const&) const;
bool collideany(std::vector<Rect> const&) const;
private:
//int* m_rect = new int[4];
std::vector<int> m_rect(4);
};

// -------------- //
int main() {
Rect r(0, 0, 20, 20), a(10, 10, 5, 5), c(21, 80, 6, 8), b(10, 11, 12, 9);
cout << a << endl;

vector<Rect> group;
group.push_back(&a);
group.push_back(&b);
group.push_back(&c);

cout << group[0] << endl;
//cout << r.collideany(group) << endl;
return 0;
}
// -------------- //

Pour le ostream comme tu le vois j'ai utilisé l'amitié pour ne pas avoir à créer une méthode print(), j'espère que c'est une bonne idée :).

Mais peut être que plonger dans linux t'éclairerait,
Je n'ai pas d'ordi en fait :/ je pense que je defilerai juste les pages wikipedia...
0
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749
15 juin 2017 à 10:02
Voici un code fonctionnel.

#include <iostream>
#include <vector> 

class Rect
{       
    private:
        std::vector<int> m_rect;
    public:
        Rect(int a, int b, int c, int d) {
            m_rect = {a, b, c, d};
        }   

        const std::vector<int> & get_vector() const {
            return m_rect;
        }   
};

template <typename T>
std::ostream & operator << (std::ostream & os, const std::vector<T> & v) {
    os << '[';
    for (auto x : v) os << ' ' << x;
    os << " ]";
    return os;
}

std::ostream & operator << (std::ostream & os, const Rect & r) {
    os << r.get_vector();
}

int main() {
    Rect r(0, 0, 20, 20), a(10, 10, 5, 5), c(21, 80, 6, 8), b(10, 11, 12, 9);
    std::cout << a << std::endl;
    std::vector<Rect> rectangles = {r, a, b, c};
    std::cout << rectangles << std::endl;
    return 0;
}


La première erreur est d'avoir précisé la taille dans la déclaration du vecteur interne à la classe Rect. C'est le constructeur qui se charge de ça, pas la déclaration.

Ensuite pour l'affichage, vu que tu te reposes sur des
std::vector
, aussi bien pour stocker les coordonnées d'un rectangle que ton groupe de rectangle, on va directement implémenter un opérateur
<<
générique sur les vectors. Attention les méthodes templates doivent toujours être écrites soit dans un header, soit dans l'unique fichier cpp qui l'utilise. Ici je ne l'utilise que dans "main.cpp" donc pas de question à se poser, je peux le mettre directement dans main.cpp.

De la même manière, tu pourrais définir des opérateurs
<<
sur tous les containers de la STL (donc
std::set<T>, std::list<T>, std::map<K, V>
, etc) si le besoin s'en faisait sentir, et tous les implémenter dans un fichier à part (mettons
stl_io.hpp
).

(mando@velvet) (~) $ g++ plop.cpp 
(mando@velvet) (~) $ ./a.out
[ 10 10 5 5 ]
[ [ 0 0 20 20 ] [ 10 10 5 5 ] [ 10 11 12 9 ] [ 21 80 6 8 ] ]


Bonne chance
0