IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

communication entre classes


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre très actif Avatar de fifafou
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2016
    Messages
    173
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 23
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Janvier 2016
    Messages : 173
    Par défaut communication entre classes
    Je programme en ce moment sur mon premier projet un peu conséquent(un clone de minecraft)
    Mais je commence a galérer de plus en plus au fur et a mesure que j'avance due au fait que je n'arrive pas a faire communiquer mes classes
    ça s'organise globalement sous forme d'un arbre:une classe mere qui contient des classes,qui peuvent contenir des classe
    le problemes est quand je doit accedera des membres d'autres classes,jusque la je gardait une reference sur un objet parent mais ça m'oblige a faire a chaque fois des doubles inclusions ce qui fini par etre sale et pas pratique(comme remonter l'abre pour descendre dans une autre branche)
    j'aurais donc besoins de conseils pour faire du code propre, surtout que je pense qu'il y a d'autres truc sale dedans
    pour l'instant il compile pas parce que je fait de grosses modifs
    Fichiers attachés Fichiers attachés

  2. #2
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    A priori, lil y a toujours intérêt à privilégier la communication "descendante" : du "conteneur" (la donnée "parent") vers le "contenu" (les données membres de la donnée parent).

    Une communication "ascendante" minimale pouvant être mise en place au travers des valeurs de retour.

    Pour la suite de mon explication, afin que les différentes relation entre classes soient claires, je vais considérer le schéma que voici
    (1) je me base sur un schéma proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
        grand mère
          /     \
         /       \
       mère     tantes
       /  \         \
      /    \         \        
    ELLE  soeurs  cousines
    et je vais m'intéresser à la donnée représentée par ELLE.

    Il y a, en effet, parfaitement du sens à ce que les "conteneurs" (parents) connaissent le type de leur "contenu" (leurs enfants), ne serait-ce que pour pouvoir déclarer les données membres au sein d'une classe.

    Il est donc "normal" de se dire que la classe "mère" connaisse le type de ELLE ainsi que le type se ses soeurs, tout comme il est "normal" que la tante connaisse connaissent mes cousines, ou que ma grand mère connaisse ma mère et mes tantes. On ne peut pas vraiment y échapper

    Par contre, il n'y a pas vraiment de raisons pour que ELLE connaisse le type de sa mère, car si il doit y avoir une communication "montante" entre ELLE et sa mère, elle devrait se limiter à la réponses aux questions posées et aux ordres donnés à ELLE par sa mère (et donc, à la valeur de retour des fonctions).

    On se rend donc tout de suite compte que, si ELLE ne doit déjà pas connaître sa mère, il y a encore moins de raison de faire en sorte que ELLE connaisse ses soeurs, ses tantes ou sa grand mère.

    Si donc, tu as besoin d'une communication plus "transversale", par exemple, si ELLE a besoin de faire savoir "quelque chose" à une de ses soeurs, c'est -- a priori -- à la mère d'interpréter la réponse donnée par ELLE et de décider (ou non) de la transmettre à une des soeurs.

    J'ai bien conscience que cette vision de la famille "étendue" a de quoi nous choquer, mais tu n'as qu'à te dire que c'est la tradition sur la planète Informatica, qui fait suite à une volonté exacerbée d'indépendance et que nous ne pourrons pas la changer

    Plus sérieusement, tu n'as qu'à te dire, en tant que développeur, que c'est le meilleur moyen de garder les choses simples, et de ne pas introduire de complexité inutile au niveau de ton projet.

    Seulement, voilà, un tel mode de communication est, finalement, beaucoup trop strict car il empêche la communication "d'urgence" entre les différents éléments de la communauté.

    C'est la raison pour laquelle chaque personne sur la planète Informatica va disposer d'un émetteur radio. De cette manière, si ELLE a "quelque chose à faire savoir" qui n'est pas forcément la réponse à l'ordre ou à la question de sa mère, il lui suffira d'utiliser cette radio pour "émettre" l'information, sans savoir (et pire encore: sans même s'inquiéter de savoir) si "quelqu'un" aura reçu l'information en question.

    Mais ca, en fait, c'est un peu le principe des stations radio comme nostalgie ou autres RFM: les gens qui ont l'émetteur émettent (à longueur de journée) sans savoir s'il y a quelqu'un qui les écoute. Et si personne ne les écoute, c'est toujours aussi bon pour eux

    En informatique, nous parlerons d'un système "de signaux et de slots", car, tout comme tu vas décider de te connecter sur la fréquence particulière de nostalgie ou de RFM parce que tu es "intéressé" par ce qu'ils disent, n'importe qui peut décider de se connecter sur "un des signaux" émis par ELLE afin de pouvoir y réagir.

    Bien sur, les restrictions à la "communication directe" en vigueur sur la planète Informatica feront que, si une des soeurs de ELLE s'intéresse à ce que ELLE a à dire, ce sera à leur mère de connecter le slot de la soeur au signal de ELLE qui intéresse la soeur. Mais le système fonctionnera a priori très bien.

    Je me suis d'ailleurs amusé à mettre un tel système -- composé en réalité d'un seul fichier d'en-tête -- de signaux et de slots au point, dont le code est disponible (avec des exemples et des tests)sur framagit (l'équivalent à github) et que tu peux réutiliser à ta guise car je ne crois pas qu'il y ait un tel système dans SFLM, qui ne présente que la notion d'événements (events)
    Enfin, il faut bien avoir conscience que, en dehors de quelques cas bien particuliers, le besoin d'une communication transversale est -- souvent -- le symptôme d'un problème de conception "plus profond", surtout si tu en arrive -- par exemple -- à vouloir faire communiquer la gestion du son avec la gestion du réseau.

    En outre (parce que j'ai quand même un tout petit peu regardé le code que tu présentes ) devoir faire appel au monde pour lui demander de nous passer l'application à laquelle on demande de nous passer la caméra afin de lui demander sa position (c'est ce que tu fais -- entre autres -- dans ta fonction ChunkLoading::testChunk) semble clairement indiquer que tu as considéré tes différentes classes comme des "ensembles de données" plutôt que les considérer comme des "fournisseurs de services", allant ainsi à l'encontre de la loi dite de Déméter.

    Mais ca, j'en parlerai plus tard, si ça t'intéresse
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Membre très actif Avatar de fifafou
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2016
    Messages
    173
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 23
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Janvier 2016
    Messages : 173
    Par défaut
    merci pour ce (gros) message bien clair
    je pense que je vais revoir la structure de mon programme pour éviter la communication transversale
    je vais voir ce que ça donne

  4. #4
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Pour y arriver, il faudra absolument que tu essaye d'appliquer beaucoup mieux les principes SOLID (dis moi si tu souhaites que je t'en parle )

    Pour donner un exemple "relativement" simple du respect du S (qui signifie SRP ou Single Responsability Principle), voici à quoi ressemble ta fonction LoadShaders(const char * vertex_file_path,const char * fragment_file_path):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    GLuint LoadShaders(const char * vertex_file_path,const char * fragment_file_path){
     
    	// Create the shaders
    	GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
    	GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
     
    	// Read the Vertex Shader code from the file
    	std::string VertexShaderCode;
    	std::ifstream VertexShaderStream(vertex_file_path, std::ios::in);
    	if(VertexShaderStream.is_open()){
    		std::stringstream sstr;
    		sstr << VertexShaderStream.rdbuf();
    		VertexShaderCode = sstr.str();
    		VertexShaderStream.close();
    	}else{
    		printf("Impossible to open %s. Are you in the right directory ?\n", vertex_file_path);
    		getchar();
    		return 0;
    	}
     
    	// Read the Fragment Shader code from the file
    	std::string FragmentShaderCode;
    	std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in);
    	if(FragmentShaderStream.is_open()){
    		std::stringstream sstr;
    		sstr << FragmentShaderStream.rdbuf();
    		FragmentShaderCode = sstr.str();
    		FragmentShaderStream.close();
    	}
     
    	GLint Result = GL_FALSE;
    	int InfoLogLength;
     
    	// Compile Vertex Shader
    	printf("Compiling shader : %s\n", vertex_file_path);
    	char const * VertexSourcePointer = VertexShaderCode.c_str();
    	glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL);
    	glCompileShader(VertexShaderID);
     
    	// Check Vertex Shader
    	glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
    	glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
    	if ( InfoLogLength > 0 ){
    		std::vector<char> VertexShaderErrorMessage(InfoLogLength+1);
    		glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
    		printf("%s\n", &VertexShaderErrorMessage[0]);
    	}
     
    	// Compile Fragment Shader
    	printf("Compiling shader : %s\n", fragment_file_path);
    	char const * FragmentSourcePointer = FragmentShaderCode.c_str();
    	glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL);
    	glCompileShader(FragmentShaderID);
     
    	// Check Fragment Shader
    	glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
    	glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
    	if ( InfoLogLength > 0 ){
    		std::vector<char> FragmentShaderErrorMessage(InfoLogLength+1);
    		glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
    		printf("%s\n", &FragmentShaderErrorMessage[0]);
    	}
     
     
     
    	// Link the program
    	printf("Linking program\n");
    	GLuint ProgramID = glCreateProgram();
    	glAttachShader(ProgramID, VertexShaderID);
    	glAttachShader(ProgramID, FragmentShaderID);
    	glLinkProgram(ProgramID);
     
    	// Check the program
    	glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
    	glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
    	if ( InfoLogLength > 0 ){
    		std::vector<char> ProgramErrorMessage(InfoLogLength+1);
    		glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
    		printf("%s\n", &ProgramErrorMessage[0]);
    	}
     
     
    	glDetachShader(ProgramID, VertexShaderID);
    	glDetachShader(ProgramID, FragmentShaderID);
     
    	glDeleteShader(VertexShaderID);
    	glDeleteShader(FragmentShaderID);
     
    	return ProgramID;
    }
    Déjà, avec ses 90 lignes de code, on peut se dire qu'elle est au moins deux fois trop longue

    Mais, en plus, tu passe ton temps à passer du vertex shader au fragment shader... Pourquoi ne déciderait-on pas de faire d'abord tout ce qui concerne un shader (par exemple, le vertex shader) avant de passer au suivent

    Remettons donc tout cela dans l'ordre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    GLuint LoadShaders(const char * vertex_file_path,const char * fragment_file_path){
            /*****************************************************************************************************/
    	GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
    	// Read the Vertex Shader code from the file
    	std::string VertexShaderCode;
    	std::ifstream VertexShaderStream(vertex_file_path, std::ios::in);
    	if(VertexShaderStream.is_open()){
    		std::stringstream sstr;
    		sstr << VertexShaderStream.rdbuf();
    		VertexShaderCode = sstr.str();
    		VertexShaderStream.close();
    	}else{
    		printf("Impossible to open %s. Are you in the right directory ?\n", vertex_file_path);
    		getchar();
    		return 0;
    	}
    	// Compile Vertex Shader
    	printf("Compiling shader : %s\n", vertex_file_path);
    	char const * VertexSourcePointer = VertexShaderCode.c_str();
    	glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL);
    	glCompileShader(VertexShaderID);
    	GLint Result = GL_FALSE;
    	int InfoLogLength;
    	// Check Vertex Shader
    	glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
    	glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
    	if ( InfoLogLength > 0 ){
    		std::vector<char> VertexShaderErrorMessage(InfoLogLength+1);
    		glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
    		printf("%s\n", &VertexShaderErrorMessage[0]);
    	}
            /*****************************************************************************************************/
    	GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
    	// Read the Fragment Shader code from the file
    	std::string FragmentShaderCode;
    	std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in);
    	if(FragmentShaderStream.is_open()){
    		std::stringstream sstr;
    		sstr << FragmentShaderStream.rdbuf();
    		FragmentShaderCode = sstr.str();
    		FragmentShaderStream.close();
    	}
    	// Compile Fragment Shader
    	printf("Compiling shader : %s\n", fragment_file_path);
    	char const * FragmentSourcePointer = FragmentShaderCode.c_str();
    	glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL);
    	glCompileShader(FragmentShaderID);
    	// Check Fragment Shader
    	glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
    	glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
    	if ( InfoLogLength > 0 ){
    		std::vector<char> FragmentShaderErrorMessage(InfoLogLength+1);
    		glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
    		printf("%s\n", &FragmentShaderErrorMessage[0]);
    	}
            /*****************************************************************************************************/
    	// Link the program
    	printf("Linking program\n");
    	GLuint ProgramID = glCreateProgram();
    	glAttachShader(ProgramID, VertexShaderID);
    	glAttachShader(ProgramID, FragmentShaderID);
    	glLinkProgram(ProgramID);
    	// Check the program
    	glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
    	glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
    	if ( InfoLogLength > 0 ){
    		std::vector<char> ProgramErrorMessage(InfoLogLength+1);
    		glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
    		printf("%s\n", &ProgramErrorMessage[0]);
    	}
    	glDetachShader(ProgramID, VertexShaderID);
    	glDetachShader(ProgramID, FragmentShaderID);
    	glDeleteShader(VertexShaderID);
    	glDeleteShader(FragmentShaderID);
    	return ProgramID;
    }
    Tu remarqueras que je n'ai rien changé en dehors de l'ordre dans lequel les différentes choses seront effectuées (en dehors du retrait des espaces non nécessaires et la modification de quelques commentaires)

    Mais n'y a-t-il rien qui te surprenne entre les lignes 3 à 31 et les lignes 33 à 55

    Hé bien, moi, si : à part le fait que l'on la valeur GL_VERTEX_SHADER dans le premier bout de code et la valeur GL_FRAGMENT_SHADER dans le deuxième, que ne va pasredéclarer Result et ]InfoLogLength et, enfin -- bien sur -- que l'on utilise des noms de fichiers différents, la logique entre ces deux bouts de code est strictement identique.

    Et ca, c'est justement le genre de chose qu'il est facile d'éviter -- que l'on devrait éviter le plus possible d'autant plus qu'il existe un acronyme issu du xp programming connu sous le nom de DRY (Don't Repeat Yourself) -- en créant "simplement" une fonction qui prenne les paramètres susceptibles de changer d'une exécution à l'autre.

    Nous pourrions donc nous retrouver avec deux fonctions proches de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    /* compile un shader OpenGL
     * @param[in] filename le nom du fichier qui sera utilisé
     * @param[in] shaderType le type shader à compiler
     * @return l'identifiant du chader compilé
     * throw std::runtime_error si "quelque chose ne se passe pas correctement"
     */
    GLuint compileShader(std::string const & filename, // parce que c'est autorisé depuis C++11
                     GLenum shaderType){
        GLuint id = glCreateShader(shaderType); 
        // Read the Vertex Shader code from the file
        std::string VertexShaderCode;
        std::ifstream shaderStream(filename); //std::ifstream a d'office std::ios::in  comme deuxième paramètre ;)
        if(! shaderStream) { // on n'a pas pu ouvrir le fichier
            std::string error{"unable to open shader file "};
            error.append(filename);
            throw std::runtime_error(error);
        }
        /* si on arrive ici, on a déjà su ouvrir le fichier ... voyons si on peut en faire quelque chose */
        std::stringstream buffer;
        buffer <<shaderStream.rdbuf();
        std::string shaderCode = buffer.str(); 
        glShaderSource(id, 1, shaderCode.c_str(), nullptr);
        glCompileShader(id);
        GLint result = GL_FALSE;
        glGetShaderiv(id, GL_COMPILE_STATUS, &Result);
        if ( result == GL_FALSE ){ // OUPPPSS, cela n'a pas compilé 
            int length;
            glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
            std::vector<char> logTab(length);
            glGetShaderInfoLog(fs, length, NULL, v.data());
            std::string error(begin(logTab), end(logTab));
            throw std::runtime_error(error);
        }
        /* si on arrive ici, c'est que la compilaiton du shader s'est bien passée
         * il ne nous reste donc qu'à renvoyer l'identifiant du shader
         */
        return id;
    }
    et
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     
    /* la fonction qui s'occupe de compiler le programme
     * sur base des deux shader (vertex et fragment)
     */
    GLuint loadShaders(std::string const & vertex, std::string const & fragment){
        auto idVertex = compileShader(vertex, GL_VERTEX_SHADER);
        auto idFragment = compileShader(fragment, GL_FRAGMENT_SHADER);
        GLuint id = glCreateProgram();
        glAttachShader(id, idVertex);
        glAttachShader(id, idFragment);
        glLinkProgram(ProgramID);
        GLint result = GL_FALSE;
        glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
        if(result == GL_FALSE){
            int length;
    	glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
                std::vector<char> logTab(length);
    		glGetProgramInfoLog(fs, length, NULL, v.data());
            std::string error(begin(logTab), end(logTab));
            throw std::runtime_error(error);
        }
        /* si on arrive ici, c'est que la liaison du programme s'est bien passée
         * il ne nous reste donc "nettoyer nos crasses"
        glDetachShader(id, idVertex);
        glDetachShader(id idFragment);
        glDeleteShader(idVertex);
        glDeleteShader(idFragment);
        return id;
    }
    Et, du coups, nous passons d'une fonction qui faisait près de 90 lignes à une fonction qui n'en fait pas 40 et une autre qui n'en fait pas 30 (malgré différents commentaires rajoutés)

    Alors, il est vrai que "gagner ving lignes de code, c'est pas énorme"...

    Mais le gros avantage à travailler de la sorte est que tu as désormais deux fonctions qui s'occupent de problèmes bien spécifiques et que, en cas d'erreur, tu sauras le code de quelle fonction aller regarder

    PS Encore une chance que j'avais parlé d'un "exemple relativement simple" hein
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Communication entre classes
    Par sunshine33 dans le forum Débuter avec Java
    Réponses: 2
    Dernier message: 03/02/2010, 21h04
  2. communication entre classes
    Par milomar dans le forum C++
    Réponses: 6
    Dernier message: 02/03/2008, 11h59
  3. Communication entre classes
    Par matteli dans le forum C++
    Réponses: 6
    Dernier message: 25/02/2008, 11h45
  4. Problème de communication entre classe
    Par FabaCoeur dans le forum Général Java
    Réponses: 4
    Dernier message: 22/01/2008, 13h50
  5. [c#] probléme de communication entre classe
    Par OpenGG dans le forum C#
    Réponses: 1
    Dernier message: 24/09/2006, 21h54

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo