# C++ et erreur Zerolink



## Mac Shiloh (15 Novembre 2004)

Bonjour !
Dans le cadre de mes études, je dois faire un projet en C++. J'ai lu plusieurs sujets sur ce forum qui disait qu'on pouvait développer avec xCode alors je me suis lancé.
Ce projet nous impose une hiérarchie de classes: on a une classe B qui dérive d'une classe A et la classe C dérive de cette classe B. Les classes A et B sont des classes abstraites. Pour l'instant, ces classes ne contiennent que les constructeurs  et je crée un objet de classe C dans le main.
Lorsque je compile, tout se passe bien mais quand j'exécute, j'ai une erreur Zerolink: unknown symbol suivi du nom de ma classe C.
Pensant que ça venait d'xCode, j'ai effectué une compil avec g++ via le Terminal. J'obtiens plusieurs erreurs du même type: 
ld: Undefined symbols:
vtable for classe_A
Et de même pour ma classe B et C.
Je vois pas du tout d'où peut venir ces erreurs sachant que j'ai bien mis des #include "classe_C.h" au début de mon main et que la compil sous xCode se passe bien. Quelqu'un aurait une idée ?
Je ne sais pas du tout à quoi correspond Zerolink alors je m'en remet totalement à vous. A quoi ça correspond et surtout à quoi ça sert ?

Merci d'avance.


----------



## ntx (15 Novembre 2004)

Bonsoir,
tu n'aurais des sources à nous montrer, ce serait plus facile à débuger.


----------



## Mac Shiloh (15 Novembre 2004)

Oui oui, bien sur. Les voici. on commence par la classe la plus haute dans la hierarchie: Objet.h


```
#ifndef OBJET_H
#define OBJET_H

class Objet{
public:  
	Objet();
	Objet(const Objet &);
	Objet(const char, const int, const int);
	virtual ~Objet();
	virtual bool canGoTo()const =0;
private: 
	char symbole;
	int coordX,coordY;
};

#endif
```

Voilà le cpp associé: 

```
#include "Objet.h"

Objet::Objet(){
	symbole='\0';
	coordX = coordY = 0;
}

Objet::Objet(const char c,const int X, const int Y){
	coordX = X;
	coordY = Y;
	symbole = c;
}
```

Et maintenant la deuxième classe dans la hierarchie: Objet_fixe.h


```
#ifndef OBJET_FIXE_H
#define OBJET_FIXE_H

#include <Carbon/Carbon.h>
#include "Objet.h"

class Objet_fixe: public Objet {
public: 
	Objet_fixe();
	Objet_fixe(const char,const int,const int);
	virtual ~Objet_fixe();
};

#endif
```

Ainsi que son cpp:

```
#include "Objet_fixe.h"

Objet_fixe::Objet_fixe():Objet(){}

Objet_fixe::Objet_fixe(const char c,const int x,const int y):Objet(c,x,y){}
```

Et enfin la dernière classe qui semble posé problème:  Roche.h


```
#ifndef ROCHE_H
#define ROCHE_H
#include <Carbon/Carbon.h>
#include "Objet_fixe.h"

class Roche:public Objet_fixe{
public: 
	Roche();
	Roche(const int,const int);
	~Roche();
	bool canGoTo() const;
};

#endif
```

et son cpp:


```
#include "Roche.h"
Roche::Roche():Objet_fixe('H',0,0){}

Roche::Roche(const int Y,const int X):Objet_fixe('H',X,Y){}

bool Roche::canGoTo() const{
	return FALSE;
}
```

Et pour finir, voilà mon main:


```
#include <iostream>
#include "Roche.h"

int main (int argc, char * const argv[]) {
    std::cout << "Hello, World!\n";
	Roche *roc = new Roche(3,9);
	if (roc->canGoTo()){
		std::cout <<"ca marche !";
	}
     delete(roc);
    return 0;
}
```

Voilà, c'est tout. Je pense avoir utilisé que du C++ standard donc je vois pas d'où peut venir mon erreur. Encore une fois, merci d'avance.


----------



## ntx (15 Novembre 2004)

> Et maintenant la deuxième classe dans la hierarchie: Objet_fixe.h
> 
> 
> ```
> ...



La fonction "bool canGoTo()const" n'est pas déclarée pour cette classe. Si je ne me trompe pas, vtable est l'objet interne qui définie la liste des fonctions membres d'une classe. Donc peut être qu'il manque une info sur cette fonction.


----------



## Mac Shiloh (15 Novembre 2004)

Je ne pensais pas qu'il fallait le rajouter ici puisque la classe ne définit pas la fonction.
Je viens de modifier mon code pour déclarer la fonction (en rajoutant virtual bool canGoTo() = 0 dans Objet_fixe.h) mais malheureusement ca ne change rien, j'ai toujours le même message d'erreur: ZeroLink: unknown symbol '__ZTV5Roche'
J'ai un doute qui me turlupine: est-ce que le fait de mettre les destructeurs de Objet et Objet_fixe en virtuel et de ne pas définir le destructeur pour la classe Roche pourrait être à l'origine de cette erreur ?


----------



## jemeor (15 Novembre 2004)

Salut,

Dans object.h tu déclares un destructeur, mais tu ne le définis pas. Essaye par exemple de remplacer

virtual ~Objet();

par 

virtual ~Objet() { }

Pareil pour les autres classes. Ce sont uniquement les fonctions virtuelles pures (= 0) qu'il n'est pas nécessaire de définir (sauf pour destructeurs).

a+


----------



## Spyro (15 Novembre 2004)

Zerolink ça fait que ça crée un exécutable qui ne contient pas les données des divers codes compilés, c'est utilisé uniquement en mode "developpement" et non "deployment". Ça a pour intérêt de permettre de sauter l'étape de linking au cours du développement de l'application. Et ça a notamment pour conséquence que l'exécutable de "développement" ainsi créé ne fonctionne que s'il est exécuté dans le répertoire où il a été créé (je me suis fait avoir une fois avec une distrib de Launcher Factory) - et que certaines erreurs sont reportées à l'exécution.

Il est possible que du coup ça pose des problèmes qui auraient été détectés au moment du linking, que tu vois donc apparaître avec g++ (et que tu aurais vus en compilant en mode "deployment" je pense ou en désactivant le zerolink).

Bon ça c'est pour les explications. Pour ton code je suis trop fatigué pour m'y plonger.
[edit] _Mais on dirait que tu as peut-être déjà une réponse _


----------



## ntx (15 Novembre 2004)

Tu as déclaré un destructeur dans la classe Roche mais tu ne l'as pas défini : pas bon. Le linker ne t'indique pas tout à gfait le bon symbole car le caractère "~" doit être masquer.

Le but de déclarer un constructeur virtuel est d'être sur qu'un objet appelera toujours son destructeur - s'il est défini - et pas celui d'une classe au-dessus à la place, ce qui pourrait entraîner une mauvaise destruction de l'objet, oubliant par exemple de detruire des membres spécifiques à la classe la plus basse.


----------



## Mac Shiloh (15 Novembre 2004)

Merci Spyro pour tes explications sur Zerolink.
Ca y est, j'ai enfin plus l'erreur de Zerolink
J'ai essayé de mettre virtual ~Objet() { } comme me le recommandait jemeor mais ca ne changeait rien, j'avais toujours l'erreur.
Effectivement, j'avais pas défini le destructeur de Roche: c'est parce que je ne vois pas quoi détruire pour l'instant ! Les données que j'ai sont dans Objet et ce sont des int et char. J'ai tout de même mis: Roche::~Roche(){} dans le cpp pour qu'il y ait un destructeur de défini et là, ca passe enfin !  
Merci beaucoup à tous de m'avoir aidé à une heure aussi tardive   
Bonne nuit


----------



## Mac Shiloh (17 Novembre 2004)

Bonsoir tout le monde. 
Je me permet de faire remonter ce thread parce que j'ai de nouveau un problème avec ce projet et comme mes classes sont déjà décrites dans ce thread, c'est plus pratique !
Voilà, j'ai une nouvelle classe Niveau qui contient un tableau à 2 dimensions d'Objet. Voici le header:


```
#include "Roche.h"

class Niveau{
public: 
	Niveau();
	~Niveau();
	void afficher() const;
	void putObject(int, int,Objet *);
	Objet* getObject(int, int);
private: 
	Objet *tab[10][10];
};
```

Et son cpp:


```
#include "Niveau.h"

Niveau::Niveau(){
	for (int i = 0;i < 10;i++){
		for (int j = 0;j < 10;j++){
			Objet *newObject = new Roche(i,j);
			tab[i][j] = newObject;
		}
	}
}

Niveau::~Niveau(){
	for (int i = 0;i < 10;i++){
		for (int j = 0;j < 10;j++){
			delete(tab[i][j]);
		}
	}
}

void Niveau::afficher() const{
	for (int i = 0;i < 10;i++){
		for (int j = 0;j < 10;j++){
			std::cout << (tab[i][j])->getSymbol();
		}
		std::cout << '\n';
	}
}

void Niveau::putObject(int i, int j,Objet *obj){
	delete (tab[i][j]);
	tab [i][j] = obj;
}

Objet* Niveau::getObject(int i, int j){
	return tab[i][j];
}
```


Jusque là, pas de problème, tout marche.
Maintenant, je dois pouvoir accéder au tableau dans la classe Roche pour pouvoir modifier le tableau. Pour se faire, je comptais mettre un pointeur vers Niveau dans la classe Roche. Je met donc un #include "Niveau.h", je compile et ca passe. Mais dès que je veux déclarer quelque chose du type: Niveau level; ca passe plus et j'obtiens une erreur disant: 'Niveau' is used as a type but not defined as a type.
Bon, je me dis: essayons autre chose et j'essaye de passer Niveau en argument dans le constructeur: 	Roche(const int,const int, Niveau);
Mais idem, erreur: type specifer omitted for parameter 'Niveau'
Comment se fait-il qu'il ne reconnaisse pas ma classe Niveau alors que j'ai bien mis #include "Niveau.h" ? Serait-ce du à l'inclusion croisée (Niveau qui inclus Roche qui inclut lui meme Niveau) que j'ai faite ?


----------



## ntx (17 Novembre 2004)

Mac Shiloh a dit:
			
		

> Bonsoir tout le monde.
> Serait-ce du à l'inclusion croisée (Niveau qui inclus Roche qui inclut lui meme Niveau) que j'ai faite ?


Oui : tu as mis "#include "Niveau.h"" dans Roche.h et "#include "Roche.h"" dans Niveau.h ?

Dans Roche.h, tu peux mettre uniquement "class Niveau;", et tu mets l'include dans Roche.cpp.

PS : Tu n'as pas mis de "guard" dans Niveau.h : attention aux inclusions multiples !!!

PS2 : les paranthèses pour le delete sont inutiles.


----------



## Spyro (17 Novembre 2004)

N'empêche les include croisés c'est un peu la base quand même...
Pourquoi tu demanderais pas à ton prof pour ce genre de questions ?
(tu as bien dit "dans le cadre de mes études" non ? )

Enfin faut pas le prendre mal hein, c'est bien d'apprendre le C++, moi aussi je l'ai fait  :rateau:

(Note que j'allais te répondre quand même mais le temps de prévisualiser mon message, et ça a déjà été fait par ntx  )


----------



## Mac Shiloh (17 Novembre 2004)

Merci, c'est exactement ce qu'il faut !!!  
Tu me sauves de plusieurs heures de prise de tete ! 

Si c'est la base, notre prof doit pas avoir la meme que vous parce qu'il nous a jamais montrer ca ! Je lui aurai bien demandé mais malheureusment on a plus le Môssieur donc a peu pas.
Et pour parfaire ma base, comment on utilise guard ?

PS: j'ai pris bonne note Spyro


----------



## ntx (17 Novembre 2004)

Mac Shiloh a dit:
			
		

> Bonsoir tout le monde.
> Voilà, j'ai une nouvelle classe Niveau qui contient un tableau à 2 dimensions d'Objet.


De pointeurs sur des "Objet" nuance !!! Es-tu sur de vouloir travailler avec des pointeurs ? Dans ce cas vérifies ta gestion de la mémoire. L'initialisation semble bonne; par contre j'ai des doutes sur la fonction putObjet ! Ceci me semble plus adéquate :

```
void Niveau::putObject(int i, int j,Objet *obj)
{
   *(tab [i][j]) = *obj;
}
```
Tu as aloué de la mémoire pour chaque élement de ton tableau dans le constructeur. Si tu détruit ton élement, tu libères la mémoire que tu as réservé et tu utilises à la place celle de l'objet que tu passe en paramètre. Attention car cet objet est extérieur à ton objet de la classe Niveau, et tu ne sais donc pas si un autre bout de la code ne va pas faire un delete dessus (ce qui serait son droit) et donc te corrompre ton tableau interne. Dommage 

IL NE FAUT JAMAIS autoriser du code extérieur d'atteindre directement des membres privés d'une classe, sinon ils ne sont plus privés. Donc toujours utiliser le mot réservé const quand on travaille sur les accesseurs des membres privés :

```
const Objet* Niveau::getObject(int i, int j){
        return tab[i][j];
}
```
et toujours faire des copies des objets et non se contenter d'affectation des pointeurs ou de références..

D'où la question : est-ce que les pointeurs sont vraiment utiles ?


----------



## ntx (17 Novembre 2004)

Mac Shiloh a dit:
			
		

> Et pour parfaire ma base, comment on utilise guard ?


Ce sont les lignes #ifndef, #define et #endif présentes dans les .h de tes autres classes. Quand l'include est utilisé pour la premiere fois la macro n'est pas définie, donc on la définie et on passe dans le code du .h.

Lors des utilisations suivantes de l'include, la macro sera définie et on ne passera plus dans le code du .h.


----------



## Mac Shiloh (17 Novembre 2004)

ntx a dit:
			
		

> Ce sont les lignes #ifndef, #define et #endif présentes dans les .h de tes autres classes. Quand l'include est utilisé pour la premiere fois la macro n'est pas définie, donc on la définie et on passe dans le code du .h.
> 
> Lors des utilisations suivantes de l'include, la macro sera définie et on ne passera plus dans le code du .h.



Okai, je savais pas que ca s'écrivait comme ca ( merci Mr le professeur   ). En fait, ils y sont dans mon code, c'est juste que je les a pas pris lors de mon copier-coller.

J'ai mis un delete dans ma fonction putObject parce que l'élément que je remplace dans le tableau, je ne l'utilise plus après. Et comme j'ai prévu de programmer mon projet, aucun delete ne sera fait en dehors de cette fonction donc aucune altération de mon tableau est possible.



			
				ntx a dit:
			
		

> De pointeurs sur des "Objet" nuance


Effectivement, je me suis mal exprimé.



			
				ntx a dit:
			
		

> D'où la question : est-ce que les pointeurs sont vraiment utiles ?



Quelle serait l'autre solution ? Utiliser directement : Objet tab [10][10] ?


----------



## ntx (17 Novembre 2004)

Mac Shiloh a dit:
			
		

> J'ai mis un delete dans ma fonction putObject parce que l'élément que je remplace dans le tableau, je ne l'utilise plus après. Et comme j'ai prévu de programmer mon projet, aucun delete ne sera fait en dehors de cette fonction donc aucune altération de mon tableau est possible.


T'es sur :


```
Niveau niveau; // Appel du constructeur de niveau, l'élément (1,1) pointe sur l'adresse 0x2000 par exemple

Roche *obj = new Roche('a',1,1); // Memoire allouer a l'adresse 0x1000 par exemple

niveau.putObjet(1,1,obj); // l'élément (1,1) de ton tableau pointe sur l'adresse 0x1000

delete obj; // l'élément (1,1) à l'adresse 0x1000 est détruit ! Ton tableau est aléré ! Dommage !
```


----------



## ntx (17 Novembre 2004)

Mac Shiloh a dit:
			
		

> Quelle serait l'autre solution ? Utiliser directement : Objet tab [10][10] ?


Pour éviter memory leaks et autres déconvenus, je pense qu'il est préférable d'utiliser les pointeurs le moins possible. Un tableau d'objets marchera tout aussi bien avec les acceseurs qui vont bien :

```
void Niveau::putObject(int i, int j,const Objet& obj)
{
        tab [i][j] = obj;
}

const Objet& Niveau::getObject(int i, int j) const
{
        return tab[i][j];
}
```


----------



## Mac Shiloh (17 Novembre 2004)

Okai, je vais y réflechir et modifier mon projet en conséquence.
Merci beaucoup !!! Je vais me remettre au boulot. Encore une longue soirée en perspective...  :sleep:


----------



## jemeor (17 Novembre 2004)

Salut,
gérer directement un tableau d'objets est effectivement moins dangereux que de gérer des pointeurs, mais il y a des aussi des inconvénients, par exemple tu ne pourra stocker que des objets de la même classe: impossible de mélanger Roche, Arbre, Alien ... et j'ai justement l'impression que c'est ce que tu veux faire.

Bon courage en tous cas.
++


----------



## Mac Shiloh (17 Novembre 2004)

Eh oui, malheureusement c'est ce que je veux faire ! Y a d'autres classes qui vont aussi se mettre dans ce tableau. Je vais garder mes pointeurs et bien faire attention à mes destructions d'objet.


----------

