Template et héritage

Résolu
ndubien Messages postés 557 Date d'inscription dimanche 25 septembre 2005 Statut Membre Dernière intervention 10 mai 2014 - 29 janv. 2012 à 01:38
ndubien Messages postés 557 Date d'inscription dimanche 25 septembre 2005 Statut Membre Dernière intervention 10 mai 2014 - 30 janv. 2012 à 18:00
Bonjour,

Je souhaiterais savoir s'il est possible de créer une classe utilisant un template "restreint" à certaines classes.

Je m'explique, je souhaiterais que le type ne puisse être qu'un "descendant" d'un type A ainsi tous mes objets seraient A et certains seraient de type une sous-classe de A. J'aimerais pouvoir faire MaClasse<classe descendante d'une classe A>.

Merci d'avance et à bientôt.
Nicolas Dubien

6 réponses

cptpingu Messages postés 3837 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 28 mars 2023 123
29 janv. 2012 à 12:49
Bonjour.

L'astuce consiste à tenter de faire "rentrer" une classe mère dans une classe fille, et voir si ça passe.
Il suffit de créer un template qui fait cela. Dans le code ci-dessous, afin d'éviter de créer une variable pour rien, je fais une méthode qui essaie de faire cette assignation, puis je pose un pointeur sur cette fonction. Si la fonction n'est pas valide, le pointeur sur fonction ne peut être posé, et il y aura erreur de compilation.
Il suffit tout simplement d'hériter de celle-ci pour en profiter.

#include 

/*!
** @struct DerivedFrom
**
** Check if Type inherits BaseType.
** Raise a compilation error if this
** constraints is not respected.
*/
template<typename Type, typename BaseType>
struct DerivedFrom
{
  typedef void(*Func)(Type*);
  static void constraints(Type* type)
  {
    BaseType* base = type;
    base = base;
  }
  DerivedFrom()
  {
    Func f = constraints;
    f = f;
  }
};

class A
{
};

class B : public A
{
};

class C
{
};


template <typename T>
class Container : private DerivedFrom<T, A>
// Pas besoin de public, un private suffit
// vu que tu n'as besoin de rien dans la classe DerivedFrom
{

};

int main()
{
  Container ca;
  Container cb;
  //Container<C> cc; // Erreur de compilation. C n'hérite pas de A.

  return 0;
}


________________________________________________________________________
Historique de mes créations, et quelques articles:
[ http://0217021.free.fr/portfolio http://0217021.free.fr/portfolio]
Merci d'utiliser Réponse acceptée si un post répond à votre question
3
cptpingu Messages postés 3837 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 28 mars 2023 123
30 janv. 2012 à 15:45
... ce que doit contenir un *.hxx par rapport à un *.cpp.

Un .hxx contient les méthodes templates et les fonctions/methodes inlines. (En gros, s'il y a marqué "template" ou "inline" = > .hxx).

Suite à votre réponse, j'aimerais savoir ce qu'il faut ou non mettre en inline

Réponse rapide:
C'est à toi de voir. Mais tout le code que tu m'a montré va entièrement dans un .hxx. Attention, "template" et "inline" ne sont pas liés, ce sont deux notions différentes. Si tu ne sais pas quoi passer en inline, ne passe rien !
"inline" veut dire: "Salut Mr le compilateur, cette méthode là, je pense qu'on pourrait la faire sauter en copiant le contenu de la méthode/fonction à la place de son appel." Le compilateur est libre de t'écouter (il arrive qu'il ne le fasse pas). De plus, même sans marquer des méthodes en inline, il prend souvent la décision de les "inliner".
En gros, un compilateur se charge tout seul de l'inlining. Tu peux lui donner un conseil, mais il n'est pas obligé de le prendre en compte. Généralement, il le fait *très très* bien tout seul. À moins d'être sur de toi, il n'est pas nécessaire de marquer une méthode en inline.

Réponse plus technique:
On passe une méthode en inline lorsque le besoin en performance se fait ressentir, et que le compilateur n'a pas été capable d'inliner une méthode (il n'est pas parfait). Ça demande une analyse de performance via des outils comme callgrind + Kcachegrind, gprof, etc... Une fois le point de contention reperé, on essaie de passer la/les méthode(s) suspecte en inline et on vérifie, s'il y a gain de performance. Il est déconseillé de passer une méthode en inline, si le gain n'est pas constaté (trop forte augmentation de la taille du binaire => potentiel souci de cache => perte de perfs, ce qui est le contraire de ce que l'on recherchait à la base). C'est évidemment une notion avancée, qui se fait vers la fin d'un cycle de développement, et demande une réelle étude.

________________________________________________________________________
Historique de mes créations, et quelques articles:
[ http://0217021.free.fr/portfolio http://0217021.free.fr/portfolio]
Merci d'utiliser Réponse acceptée si un post répond à votre question
3
ndubien Messages postés 557 Date d'inscription dimanche 25 septembre 2005 Statut Membre Dernière intervention 10 mai 2014 4
29 janv. 2012 à 22:26
Bonjour,

Merci pour la technique, ça marche impeccable.

J'ai dorénavant un nouveau soucis lié encore une fois aux templates (c'est pourquoi je n'ouvre pas de nouveau poste) :

Critere.hpp
class Critere : public ZoneComposite<Zone>
{
    public:
        Critere( std::string nom= "" );
        virtual ~Critere();
};


Critere.cpp
Critere::Critere( string nom ) : ZoneComposite<Zone>(nom)
{
}

Critere::~Critere()
{
}


ZoneComposite.hpp
template <typename T  = ZoneAbstraite>
class ZoneComposite : public ZoneAbstraite, private DerivedFrom<T, ZoneAbstraite>
{
    public:
        ZoneComposite( std::string nom="" );
        virtual ~ZoneComposite();

        virtual bool hasPoint( int const& x, int const& y ) const; // le point appartient-t'il à l'une des zones
        virtual std::ostream& afficher( std::ostream& flux, unsigned int const& etage ) const;

    protected:
        std::vector<T*> m_zones;

};

Remarque: Toutes les méthodes de ZoneComposite sont bien définies

Voici les erreurs:
Critere.cpp|| undefined reference to `ZoneComposite<Zone>::~ZoneComposite()'|
Critere.o||In function `Critere::Critere(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)':|
Critere.cpp|| undefined reference to `ZoneComposite<Zone>::ZoneComposite(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'|
Critere.o||In function `Critere::~Critere()':|
Critere.cpp|| undefined reference to `ZoneComposite<Zone>::~ZoneComposite()'|
Critere.o:(.rodata._ZTV7Critere[vtable for Critere]+0x10)||undefined reference to `ZoneComposite<Zone>::hasPoint(int const&, int const&) const'|
Critere.o:(.rodata._ZTV7Critere[vtable for Critere]+0x14)||undefined reference to `ZoneComposite<Zone>::afficher(std::basic_ostream<char, std::char_traits<char> >&, unsigned int const&) const'|


Merci d'avance et à bientôt.
Nicolas Dubien
0
cptpingu Messages postés 3837 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 28 mars 2023 123
30 janv. 2012 à 10:13
Est-ce que tu mets bien tout tes codes "inlinés" et de template dans un .hpp ? Si tu mets du code dans un .cpp, forcément, tu auras des erreurs de "link". (On utilise généralement la technique du .hxx pour mettre du code dans un .hpp proprement, cf mes sources).

Quelques conseils:
- Évite les "using namespace", voir: http://0217021.free.fr/portfolio/axel.berardino/articles/bon-usage-using-namespace
- int const& y => autant passer directement un "int" ou à la limite un "const int". La référence est généralement inutile sur un type pod.
- std::string nom => Là en revanche tu peux mettre: const std::string& nom

________________________________________________________________________
Historique de mes créations, et quelques articles:
[ http://0217021.free.fr/portfolio http://0217021.free.fr/portfolio]
Merci d'utiliser Réponse acceptée si un post répond à votre question
0

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

Posez votre question
ndubien Messages postés 557 Date d'inscription dimanche 25 septembre 2005 Statut Membre Dernière intervention 10 mai 2014 4
30 janv. 2012 à 14:25
Bonjour,

Suite à votre réponse, j'aimerais savoir ce qu'il faut ou non mettre en inline et ce que doit contenir un *.hxx par rapport à un *.cpp.

ZoneComposite.cpp
#include 

#include "ZoneComposite.hpp"

/*****************/
/* Constructeurs */
/*****************/

template <typename T>
ZoneComposite<T>::ZoneComposite( std::string nom ) : ZoneAbstraite(nom)
{
}

template <typename T>
ZoneComposite<T>::~ZoneComposite()
{
}

template <typename T>
bool ZoneComposite<T>::hasPoint( int const& x, int const& y ) const
{
    for( unsigned int i(0) ; i != m_zones.size(); i++ )
        if( m_zones[i]->hasPoint(x,y) )
            return true;

    return false;
}

template <typename T>
std::ostream& ZoneComposite<T>::afficher( std::ostream& flux, unsigned int const& etage ) const
{
    for( unsigned int i(0) ; i!=etage ; i++ )
        flux << "\t";

    flux << "[Zone Composite `" << getNom() << "` :";
    for( unsigned int i(0) ; i!=m_zones.size() ; i++ )
    {
        flux << "\n";
        m_zones[i]->afficher( flux, etage+1 );
    }
    flux << "\n]";

    return flux;
}


Pour ce qui est des références, je les supprimerai dés que j'aurai supprimé mon premier problème. J'en profite également afin de regarder vos sources.

Merci d'avance et à bientôt.
Nicolas Dubien
0
ndubien Messages postés 557 Date d'inscription dimanche 25 septembre 2005 Statut Membre Dernière intervention 10 mai 2014 4
30 janv. 2012 à 18:00
Encore merci pour vos réponses.
---
Nicolas Dubien
0
Rejoignez-nous