[C++] Instanciation d1 classe abstraite oO !?

Fermé
Sir Bedevere Messages postés 3 Date d'inscription lundi 2 juin 2008 Statut Membre Dernière intervention 3 juin 2008 - 3 juin 2008 à 10:59
mamiemando Messages postés 33077 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 18 avril 2024 - 4 juin 2008 à 14:20
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 ?

8 réponses

Char Snipeur Messages postés 9696 Date d'inscription vendredi 23 avril 2004 Statut Contributeur Dernière intervention 3 octobre 2023 1 297
3 juin 2008 à 11:42
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 ?
0
Sir Bedevere Messages postés 3 Date d'inscription lundi 2 juin 2008 Statut Membre Dernière intervention 3 juin 2008
3 juin 2008 à 14:44
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.
0
Char Snipeur Messages postés 9696 Date d'inscription vendredi 23 avril 2004 Statut Contributeur Dernière intervention 3 octobre 2023 1 297
3 juin 2008 à 15:22
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.
0
mamiemando Messages postés 33077 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 18 avril 2024 7 748
3 juin 2008 à 16:10
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 :
#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
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
Char Snipeur Messages postés 9696 Date d'inscription vendredi 23 avril 2004 Statut Contributeur Dernière intervention 3 octobre 2023 1 297
3 juin 2008 à 16:34
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 :
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;
    }
0
mamiemando Messages postés 33077 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 18 avril 2024 7 748
3 juin 2008 à 20:29
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).
(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>)
0
Char Snipeur Messages postés 9696 Date d'inscription vendredi 23 avril 2004 Statut Contributeur Dernière intervention 3 octobre 2023 1 297
4 juin 2008 à 08:30
C'est bien ce que je disais "si aucun autre constructeur n'est défini", là tu défini plop_t(uint).
Et d'ailleurs, on vois lors de la compilation que ça ne concerne pas le constructeur par copie.
0
mamiemando Messages postés 33077 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 18 avril 2024 7 748
4 juin 2008 à 14:20
ok
0