Salut,
Pour bien comprendre l'idée, tout ce que tu place entre les accolades qui délimitent ta classe devient automatiquement membre de ta classe (qu'il s'agisse d'une fonction ou d'une donnée), et, à ce titre, dépendra d'une instance particulière de ta classe.
Les deux fois deux points :: représentent ce que l'on appelle "l'opérateur de portée" que l'on utilise lorsque l'on doit utiliser ce que l'on appelle le "nom pleinement qualifié" de quelque chose :
- quand on veut accéder à une fonctionnalité qui se trouve dans un espace de nom (par exemple Communication::ACPUSerialConnection laConnection;
- quand on veut définir le comportement d'une fonction membre (comme dans le fichier ACPUSerialConnection.cpp )
- quand on a affaire à une fonction qui ne dépend d'aucune instance de la classe (j'en parlerai plus tard)
Pour pouvoir accéder à un membre de la classe (dans la limite de l'accessibilité de la fonction ou de la donnée), tu as besoin d'une instance de la classe ( une variable du type de la classe en question ) et tu y accède, classiquement, à l'aide de l'opérateur "point" . (si tu dispose de l'instance de la classe sous forme de valeur ou sous forme de référence) ou à l'aide de l'opérateur "fleche" -> si l'instance en question est un pointeur).
Par exemple:
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
| /* Soit la classe */
class MaClasse{
public:
void foo(){ /* ... */}
};
/* soit une fonction prenant une référence sur une instance existante de la classe */
void bar(MaClasse & ref){
/* on peut appeler MaClasse::foo() à partir de ref en utilisant le point '.'*/
ref.foo();
}
/* et une fonction qui prend une instance de la classe sous forme de pointeur */
void truc(MaClasse & ptr){
/* on peut appeler MaClasse::foo() à partir de ptr en utilisant la flèche '->'*/
ptr->foo()
}
int main(){
/* on a besoin d'une instance de la classe */
MaClasse obj;
/* on peut transmettre cette instance à la fonction bar, qui va faire appel à la fonction foo */
bar(obj);
/* mais on peut aussi appeler la fonction foo directement à partir de obj */
obj.foo();
/* Quand on n'a pas le choix, on travaille parfois avec des pointeurs (de préférence intelligents) */
std::unique_ptr<MaClasse> ptr = std::make_unique<MaClasse>();
truc(ptr.get());
} |
Enfin, il faut savoir qu'il existe deux types très particuliers de fonctions membres:
- les fonctions virtuelles, qui permettent de redéfinir le comportement de la fonction dans une classe dérivée (et d'obtenir un comportement polymorphe) et
- les fonctions statiques, qui ne dépendent d'aucune instance de la classe en particulier (mais qui, du coup, ne peuvent pas accéder aux données et au fonctions "normales", vu qu'elle ne dépend d'aucune instance).
Les fonctions virtuelles s'utilisent exactement comme les autres fonctions membres : il n'y a clairement que le comportement (ce qu'elles vont faire) qui va changer, par exemple:
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
| /* soit une classe de base */
class Base{
public:
/* et une fonction dont on veut pouvoir redéfinir le comportement dans les classes dérivées */
virtual void printMe() const{
std::cout<<"I'm a base\n";
}
};
/* et une autre classe qui hérite (dérive) de la classe de base */
class Derived : public Base{
public:
/* pour laquelle on redéfini le comportement de printMe
* Note que, depuis C++11, on peut la marquer comme override, pour forcer le compilateur
* à s'assurer qu'il s'agit bien de redéfinir le comportement d'une fonction existant dans la classe
* de base et pour laquelle une telle redéfinition est possible
*/
void printMe() const override{
std::cout<<"I'm a derived\n";
}
};
/* une fonction libre qui prend une base comme paramètre
* (sous forme de référence, pour profiter du polymorphisme
*/
void callMe(Base const & b){
/* et qui fait appel à printMe */
b.printMe();
}
int main(){
/* une instance de base */
Base laBase;
/* que l'on transmet à callMe */
callMe(laBase); // affiche "I'm a base"
/* et une instance de Derived */
Derived laDerivee;
/* qui peut -- grace à l'héritage -- être substituée
* à une Base, partout ou une instance de Base est attendue
*/
callMe(laDerivee); // ca marche!
// mais affiche "I'm a Derived"
} |
Pour ce qui est des fonctions statiques, vu qu'elles ne dépendent d'aucune instance particulière de la classe, il est fréquent que l'on utilise ... le nom pleinement qualifié de la fonction, sous une forme proche de
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| /* une classe pour l'exemple */
class MaClasse{
public:
/* qui expose une fonction statique */
static laFonction();
};
int main(){
/* comme laFonction ne dépend d'aucune instance particulière,
* il est fréquent que nous y faisons appel en utilisant le nom pleinement
* qualifié de la fonction :
*/
MaClasse::laFonction();
/* mais, si le hasard veut que l'on dispose d'une instance de la classe
* on peut y faire appel d'une manière tout à fait normale
*/
MaClasse obj;
obj.laFonction();
} |
NOTA : j'ai pris la peine d'écrire énormément de commentaires dans le code. Ils fournissent énormément d'informations utiles et nécessaires à la compréhension. N'hésites donc pas à les lire 
Pour ce qui est de ton problème particulier, le compilateur te fourni toutes les informations dont tu as besoin pour le corriger, même si, sur ce coup, le message n'est pas forcément très clair 
Quand il te dit
Erreur C2653: 'ACPUSerialConnection': l'identifiant n'est ni un nom de classe ni un nom d'espace de nom.
En gros, il se plaint de ne pas connaitre le terme ACPUSerialConnection et, dans un sens, c'est tout à fait normal car tu as placé ta classe dans l'espace de noms Communications.
Cet espace de noms va créer une sorte de "boite de rangement" dans laquelle tu as placé ta classe ACPUSerialConnection, qui n'est, du coup, plus visible "du dehors de la boite" (comprend : des fonctions qui ne font pas partie de l'espace de noms). Pour pouvoir accéder à cette classe, tu doit donc utiliser le noms pleinement qualifé qui permet d'y avoir accès depuis l'endroit où tu te trouve.
La fonction main se trouvant dans "l'espace de noms global" (l'espace de noms qui contient tous les autres espaces de noms, mais qui n'a pas de nom particulier, lui-même), pour pouvoir accéder à cette classe, tu devrais utiliser la forme de Communications::ACPUSerialConnection /* ... */Mais cela ne résoudra qu'une partie de ton problème, car tu as utilisé ... l'opérateur de portée pour faire appel à la fonction processRequests. Or, cet opérateur particulier ne fonctionne que ... pour les fonctions statiques alors que processRequests est une fonction ... virutelle.
Pour pouvoir faire appel à cette fonction, tu dois donc disposer d'une instance de la classe ACPUSerialConnection, ce qui donnerait au final un code proche de
1 2 3 4
| /* on crée une instance de la classe, à moins qu'on en dispose "par ailleur" */
ACPUSerialConnection connection;
/* on peut maintenant faire appel à la fonction */
bool result = connection.processRequests(); |
Partager