2010-05-11 5 views
3

J'essaie de dériver une nouvelle classe à partir d'une ancienne classe. La déclaration de classe de base ressemble à ceci:Résolution de ce pointeur ambigu en C++

class Driver : public Plugin, public CmdObject 
{ 
protected: 
    Driver(); 

public: 
    static Driver* GetInstance(); 
    virtual Engine& GetEngine(); 
public: 
    // Plugin methods... 
    virtual bool InitPlugin (Mgr* pMgr); 
    virtual bool Open(); 
    virtual bool Close(); 

    // CmdObject 
    virtual bool ExecObjCmd(uint16 cmdID, uint16 nbParams, CommandParam *pParams, CmdChannelError& error); 

    Mgr *m_pMgr; 

protected: 
    Services *m_pServices; 
    Engine m_Engine; 
}; 

Son constructeur ressemble à ceci:

Driver::Driver() : 
    YCmdObject("Driver", (CmdObjectType)100, true), 
    m_Engine("MyEngine") 
{ 
    Services *m_pServices = NULL; 
    Mgr *m_pMgr = NULL; 
} 

Alors, quand je crée ma classe dérivée, j'ai essayé d'hériter simplement de la classe de base:

class NewDriver : public Driver 

et copier le constructeur:

NewDriver::NewDriver() : 
    CmdObject("NewDriver", (EYCmdObjectType)100, true), 
    m_Engine("MyNewEngine") 
{ 
    Services *m_pServices = NULL; 
    Mgr *m_pMgr = NULL; 
} 

Le compilateur (VisualDSP ++ 5.0 de Analog Devices) n'a pas aimé ceci:

".\NewDriver.cpp", line 10: cc0293: error: indirect nonvirtual base 
     class is not allowed 
CmdObject("NewDriver", (EYCmdObjectType)100, true), 

Cela fait sens, alors j'ai décidé d'hériter directement de plug-in et CmdObject. Pour éviter de multiples problèmes d'ambiguïté de l'héritage (donc je pensais), je l'héritage virtuel:

class NewDriver : public Driver, public virtual Plugin, public virtual CmdObject 

Mais, dans la mise en œuvre d'une méthode virtuelle dans NewDriver, j'ai essayé d'appeler la méthode Mgr :: registerPlugin qui prend Plugin *, et j'ai obtenu ceci:

".\NewDriver.cpp", line 89: cc0286: error: base class "Plugin" is 
     ambiguous 
if (!m_pMgr->RegisterPlugin(this)) 

Comment ce pointeur est-il ambigu et comment le résoudre?

Merci,

--Paul

Répondre

4

Si vous dérivez de Driver, vous ne devez pas appeler les constructeurs de Driver de bases explicitement:

class NewDriver : public Driver { /* ... */ }; 
NewDriver::NewDriver() : Driver() {} 

Le constructeur de Driver alors initialise ses propres bases, vous n'avez pas à le faire et ne devriez pas le faire directement.
Si elle doit se comporter différemment, laisser prendre des paramètres:

class Driver : /* ... */ { 
public: 
    Driver(const std::string& name /* , ... */) 
     : CmdObject(name /* , ... */) 
    {} 
    // ... 
}; 

NewDriver::NewDriver() : Driver("NewDriver" /* , ... */) {} 
+3

Strictement parlant, si la classe de base ctor ne prend aucun paramètre, vous n'avez même pas besoin de le mettre dans le liste d'initialisation car il sera construit automatiquement par défaut. –

+0

@dash, Ce n'est pas tout à fait exact (du moins pas avec g ++). Les types virtuellement hérités seront initialisés avant les types non-virtuellement hérités. Ainsi, si l'ordre d'initialisation diffère entre l'ordre implicite (de la hiérarchie d'héritage physique) et physique, certains compilateurs commenceront au moins à émettre des avertissements sur l'ordre d'initialisation réorganisé (même si vous omettez les listes d'initialisation et autorisez le compilateur à faire la commande elle-même). –

+0

J'ai simplement légèrement modifié le libellé, je ne veux pas entrer dans toutes les autres intrications possibles ici. –

0

Je ne suis pas certain que l'introduction de l'héritage virtuel est la façon dont vous voulez aller ici. L'erreur initiale que vous avez eu était valide - vous essayez d'appeler le ctor CddObject() à partir de "above" la classe Driver. Pouvez-vous ajouter un autre ctor à la classe Driver?

1

Georg a la bonne réponse, il n'est définitivement pas nécessaire de jouer avec l'héritage multiple dans ce cas.

En utilisant multiple inheritance, en particulier pour créer le dreaded diamond est fortement découragé, et conduira à beaucoup de confusion et de frustration pour la grande majorité des programmeurs C++.

0

Si vous aviez des raisons de vouloir hériter à nouveau de ces classes, vous ne devriez pas utiliser l'héritage virtuel. Si vous souhaitez utiliser l'héritage virtuel, vous devez le spécifier sur les classes more-base. Cependant, vous ne voulez pas utiliser l'héritage virtuel ici. Il suffit de laisser de côté l'initialiseur des classes de base, puisque Driver le fait déjà.Dans votre exemple, vous n'avez pas du tout besoin d'un constructeur NewDriver, et si votre implémentation en a réellement besoin, alors seulement sera utilisé pour initialiser les variables membres de NewDriver et il devrait s'attendre à ce que Driver fasse la bonne chose. Si Driver ne fait pas la bonne chose, il suffit de lui donner un constructeur qui prend les paramètres appropriés et que ce constructeur fasse ce qu'il faut. Par exemple.

class Driver : public Stuff 
{ 
public: 
    Driver(const char* enginename) : Stuff(99), m_Engine(enginename) { } 

private: 
    Engine m_Engine; 
}; 

class NewDriver : public Driver 
{ 
public: 
    NewDriver() : Driver("New Driver!") { } // yes, Stuff gets initialized with 99 automatically! 
}; 
0

Le CmdObject au sein Driver devrait être suffisant. Ajustez Driver pour avoir un constructeur protected qui peut personnaliser le CmdObject dans les limites spécifiées.

class Driver ... 
protected: 
    Driver(std::string driverName, std::string engineName); 

...

Driver::Driver(std::string driverName, std::string engineName) : 
    YCmdObject(driverName, (CmdObjectType)100, true), 
    m_Engine(engineName) { 

...

NewDriver::NewDriver() : 
    Driver("NewDriver", "MyNewEngine") { 
    ... 
} 
1

Si vous créez une hiérarchie:

class A {public: a(int i){} }; 
class B : public A {public: b(){} }; 
class C : public B {public: c(); }; 

Vous ne pouvez pas passer des arguments au constructeur de A forment directement les C de constructeur

C::C() : A(5) {} // illegal 

sauf si A est une classe de base virtuelle de B qui est un cas particulier et dans ce cas, il est utile. S'il vous plaît vérifier les liens fournis par Tim Sylvester.

Le fait que le compilateur mentionne ce cas ne signifie pas que c'est une solution pour vous.

Si vous créez une hiérarchie comme dans Votre code, vous avez 2 sous-objets de type Plugin et 2 sous-objet de type CmdObject dans votre objet NewDriver. Dans ce cas, si vous essayez de rediriger le pointeur this de type NewDriver* par exemple vers le type Plugin*, le compilateur n'a aucune idée de comment ajuster l'adresse, car il ne sait pas lequel des 2 sous-objets présents dans votre objet NewDriver ce pointeur est censé pointer vers. C'est là que l'ambiguïté mentionnée dans le message d'erreur vient forme. Il y a un moyen de le dire au compilateur, mais je pense que le désordre que je décris devrait vous convaincre déjà que ce n'est pas le cas.

1

La règle d'or de l'héritage multiple - TOUS bases publiques de TOUTES les classesMUST être virtuelles. Tant que vous suivez cette règle, l'héritage multiple fonctionnera correctement. Dans votre cas, vous obtenez les erreurs de classe de base ambiguës car Plugin n'est pas déclaré virtual dans Driver