[C++] Instanciation d1 classe abstraite oO !?
Hello,
Je suis perplexe. J'ai codé un programme où j'ai une classe abstraite générique, pour laquelle je ne veux pas définir de constructeur par défaut. L'idée est d'en dériver plusieurs classes filles qui en implémentent les fonctions de différentes manières. J'ai donc écrit ça :
template <typename T>
class Analyseur {
private:
Analyseur(){std::cout << "plop !!!" << std::endl;}
public:
Echantillon<T> *analyse(Echantillon<double> *fft) = 0;
};
template <typename T>
class Analyseur_Copie : public Analyseur<T> {
// Je voudrais définir ici un constucteur et surcharger analyse()
};
Problème : Dans le programme de test que j'ai écrit, la ligne "Analyseur A()" ne semble pas poser de problèmes à mon compilateur, il fait ... quelque chose, je ne sais pas quoi. En tout cas à l'exécution, je ne vois aucun "plop !!!" s'afficher, ce qui tendrait à prouver que le constructeur par défaut que j'ai écrit n'a pas été utilisé ...
D'où ma question, qui peut se résumer en trois lettres : wtf !?
Ou bien, de façon plus détaillée, en trois points :
* Pourquoi puis-je instancier une classe abstraite ? Y a-t-il un rapport avec la généricité ?
* Pourquoi puis-je instancier une classe avec un constructeur par défaut non visible ?
* Qu'est-ce que fait le programme quand il "crée" une instance de Analyseur exactement ?
Je suis perplexe. J'ai codé un programme où j'ai une classe abstraite générique, pour laquelle je ne veux pas définir de constructeur par défaut. L'idée est d'en dériver plusieurs classes filles qui en implémentent les fonctions de différentes manières. J'ai donc écrit ça :
template <typename T>
class Analyseur {
private:
Analyseur(){std::cout << "plop !!!" << std::endl;}
public:
Echantillon<T> *analyse(Echantillon<double> *fft) = 0;
};
template <typename T>
class Analyseur_Copie : public Analyseur<T> {
// Je voudrais définir ici un constucteur et surcharger analyse()
};
Problème : Dans le programme de test que j'ai écrit, la ligne "Analyseur A()" ne semble pas poser de problèmes à mon compilateur, il fait ... quelque chose, je ne sais pas quoi. En tout cas à l'exécution, je ne vois aucun "plop !!!" s'afficher, ce qui tendrait à prouver que le constructeur par défaut que j'ai écrit n'a pas été utilisé ...
D'où ma question, qui peut se résumer en trois lettres : wtf !?
Ou bien, de façon plus détaillée, en trois points :
* Pourquoi puis-je instancier une classe abstraite ? Y a-t-il un rapport avec la généricité ?
* Pourquoi puis-je instancier une classe avec un constructeur par défaut non visible ?
* Qu'est-ce que fait le programme quand il "crée" une instance de Analyseur exactement ?
A voir également:
- [C++] Instanciation d1 classe abstraite oO !?
- Sti2d classe poubelle - Forum Études / Formation High-Tech
- Retrouver la liste des élèves d'une classe - Forum Discussions générales
- Classe ram - Guide
- Ouvrez cette page. dans le code de la page, modifiez la couleur de fond de la classe .pix. un code de 4 chiffres doit apparaître dans la grille. lequel ? ✓ - Forum Programmation
- Bluetooth mercedes classe a 2005 - Forum Autoradio
8 réponses
Salut.
En effet, je suis perplexe aussi.
Que se passe t il si tu essai d'utliser A ? A.analyse(...)
Je ne comprends même pas pourquoi il compile.
si Analyseur<double> A(); fonctionne, il y a vraiment un problème. Quel est le compilateur ?
En effet, je suis perplexe aussi.
Que se passe t il si tu essai d'utliser A ? A.analyse(...)
Je ne comprends même pas pourquoi il compile.
si Analyseur<double> A(); fonctionne, il y a vraiment un problème. Quel est le compilateur ?
Alors en effet, ce n'est pas "Analyseur A()" mais bien "Analyseur<double> A()" que j'ai mis dans mon programme et qui compilait.
Je viens de voir aussi que je n'avais pas mis le mot-clé "virtual" devant la déclaration d'analyse(). Une fois rajouté ce mot-clé, le compilo (c'est g++ 4.2 au fait) jette bien une erreur parce que j'essaie d'instanciser une classe abstraite. ouf !
Toutefois, l'instanciation d'une classe dérivée avec le constructeur par défaut fonctionne toujours, et je ne comprends pas pourquoi.
Je viens de voir aussi que je n'avais pas mis le mot-clé "virtual" devant la déclaration d'analyse(). Une fois rajouté ce mot-clé, le compilo (c'est g++ 4.2 au fait) jette bien une erreur parce que j'essaie d'instanciser une classe abstraite. ouf !
Toutefois, l'instanciation d'une classe dérivée avec le constructeur par défaut fonctionne toujours, et je ne comprends pas pourquoi.
Pour moi, ta class Analyseur est vide : elle est abstraite et ne contient aucune donnée membre. Il n'y a pas besoin d'un constructeur pour avoir une instance.
En lisant un cour d'assembleur, il parlai justement des notions de structure et de classe en C/C++.
Un structure (ou classe) est interprété par le compilateur comme une espèce de tableau avec des membres de taille variable. Les fonctions membre d'une classe, sont en fait des fonctions classiques avec un argument cacher :
type classA::fct(a,b) <=> type fct(classA&,a,b)
Donc, une class ne contenant que des fonctions, ne prend pas d'espace mémoire (enfin, c'est une image) donc n'a pas besoin de constructeur.
Mais ce n'est que mon avis, il faudrait vérifier dans les spécifications de l'ISO C++ et ne pas oublié que tout compilateur s'écarte un peu de ces spécifications, en particulier sur ces problèmes assez marginaux.
En lisant un cour d'assembleur, il parlai justement des notions de structure et de classe en C/C++.
Un structure (ou classe) est interprété par le compilateur comme une espèce de tableau avec des membres de taille variable. Les fonctions membre d'une classe, sont en fait des fonctions classiques avec un argument cacher :
type classA::fct(a,b) <=> type fct(classA&,a,b)
Donc, une class ne contenant que des fonctions, ne prend pas d'espace mémoire (enfin, c'est une image) donc n'a pas besoin de constructeur.
Mais ce n'est que mon avis, il faudrait vérifier dans les spécifications de l'ISO C++ et ne pas oublié que tout compilateur s'écarte un peu de ces spécifications, en particulier sur ces problèmes assez marginaux.
A priori en C++ il faut implémenter un constructeur par défaut si celui-ci est explicitement appelé (par exemple les containers de la STL l'appellent explicitement).
De plus ce constructeur doit explicitement appeler les constructeurs des classes mères (ceci différencie le C++ du java, mais c'est grâce à ce genre de chose qu'on peut faire du multi-héritage en C++).
Exemple :
Donne :
Je te rappelle aussi que les membres de classes pouvant être initialisés avant l'accolade ouvrante du constructeur doivent l'être, et ce conformément à l'ordre dans lequels ils sont déclarés. AInsi :
Bonne chance
De plus ce constructeur doit explicitement appeler les constructeurs des classes mères (ceci différencie le C++ du java, mais c'est grâce à ce genre de chose qu'on peut faire du multi-héritage en C++).
Exemple :
#include <iostream> template <typename T> class mere_t{ public: mere_t(){ std::cout << "je suis ta mère !" << std::endl; } }; template <typename T> class fille_t : public mere_t<T>{ public: fille_t():mere_t<T>(){ std::cout << "nooooon !" << std::endl; } }; int main(){ fille_t<int> f; return 0; }
Donne :
je suis ta mère ! nooooon !
Je te rappelle aussi que les membres de classes pouvant être initialisés avant l'accolade ouvrante du constructeur doivent l'être, et ce conformément à l'ordre dans lequels ils sont déclarés. AInsi :
class plop_t{ protected: int i; // d'abord i char c; // ensuite c float f; // enfin f public: plop_t(int i0=0,float f0,char c0='x'): i(i0),c(c0),f(f0) // d'abord i, ensuite , enfin f { // code du constructeur (éventuellement vide) } };
Bonne chance
Vous n’avez pas trouvé la réponse que vous recherchez ?
Posez votre question
Il me semble que le constructeur par défaut est créé automatiquement si aucun constructeur n'est fourni au compilateur, un peu comme operator= et le destructeur qui sont définis automatiquement.
Par contre, dès que l'on défini un constructeur, le constructeur par défaut (sans argument) doit être défini explicitement si on veux qu'il soit utilisable.
Le problème de Bedevere (je pense) c'est qu'il définit un constructeur par défaut privé et que cela ne crée pas d'erreur lors de la déclaration de la variable. Un constructeur privé empêche la déclaration d'un objet.
Pour s'en convaincre, le code suivant compiler avec gcc 3.2.3 :
Par contre, dès que l'on défini un constructeur, le constructeur par défaut (sans argument) doit être défini explicitement si on veux qu'il soit utilisable.
Le problème de Bedevere (je pense) c'est qu'il définit un constructeur par défaut privé et que cela ne crée pas d'erreur lors de la déclaration de la variable. Un constructeur privé empêche la déclaration d'un objet.
Pour s'en convaincre, le code suivant compiler avec gcc 3.2.3 :
class A{ A(){}; int a; } struct B :public A{ B(){}; //ou B():A(){}; int b; } int main() { A a; // erreur appel direct à un constructeur privé B b; // erreur aussi, B fesant appel au constructeur privé A() return 0; }
Il me semble que le constructeur par défaut est créé automatiquement si aucun constructeur n'est fourni au compilateur
Oui mais non. A priori il faut partir du principe qu'il n'y a pas de constructeur par défaut construit automatiquement (contrairement à = ou au destructeur).
De plus l'appel aux constructeur(s) mère(s) n'est a priori pas fait par défaut (cf message <1>)
Oui mais non. A priori il faut partir du principe qu'il n'y a pas de constructeur par défaut construit automatiquement (contrairement à = ou au destructeur).
(mando@aldur) (~) $ cat plop.cpp class plop_t{ protected: unsigned x; public: plop_t(unsigned x0):x(x0){} }; int main(){ plop_t p = plop_t(); return 0; } (mando@aldur) (~) $ g++ -W -Wall plop.cpp plop.cpp: In function ‘int main()’: plop.cpp:9: error: no matching function for call to ‘plop_t::plop_t()’ plop.cpp:5: note: candidates are: plop_t::plop_t(unsigned int) plop.cpp:1: note: plop_t::plop_t(const plop_t&) plop.cpp:9: warning: unused variable ‘p’ (mando@aldur) (~) $ g++ --version g++ (GCC) 4.2.4 (Debian 4.2.4-1) Copyright (C) 2007 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
De plus l'appel aux constructeur(s) mère(s) n'est a priori pas fait par défaut (cf message <1>)