2010-11-21 21 views
0

J'apprends le C++ et je suis coincé avec un problème. J'ai besoin d'un moyen d'utiliser une sous-classe spécifique dans la classe de base. Est-ce logique ou j'utilise une mauvaise approche? SelectBrand devrait sélectionner la sous-classe, comment puis-je le faire?Sélectionner la sous-classe à partir de la classe de base ... possible?

Ci-dessous mes classes simplifiées:


----- 
class Protocol { 

public: 

    Protocol() {}; 
    ~Protocol() {}; 
    int openPort(); 
    int readPort(char *buffer); 

..... 

private: 

     Protocol (const Protocol&); 
}; 

int Protocol::openPort() {......}; 

int Protocol::readPort() {.........}; 

/***********************************************************************************/ 

class Device{ 

public: 

     Device(Protocol& port):_protocol(port){} 
     ~Device(); 
     virtual int getEvent(char *buffer) { return -1; } 
     int Device::selectBrand(); 

     .............. 

protected: 

     Protocol& _protocol; 

private: 

     int brand; 
     Device(const Device&orig); 
}; 

Device::~Device() {} 

int Device::selectBrand() { 

     ...... 

     switch (X) 

      case 1: 

        "use subclass Brand_B" 

      case 2: 

        "use subclass Brand_B" 

     ....... 

} 

/***********************************************************************************/ 

class Brand_A:public Device { 

public: 

     Brand_A(Protocol& port); 
     ~Brand_A(); 
     int getEvent(void *rawData); 

private: 

     Brand_A(const Brand_A&); 
}; 

Brand_A::Brand_A(Protocol& port):Device(port) {} 

Brand_A::~Brand_A() {} 

int Brand_A::getEvent(void *rawData) { 


      .... readPort(......); 

} 

/***********************************************************************************/ 

class Brand_B:public Device { 

public: 

     Brand_B(Protocol& port); 
     ~Brand_B(); 
     int getEvent(void *rawData); 

private: 

     Brand_B(const Brand_B&); 

}; 

Brand_B::Brand_B(Protocol& port):Device(port) {} 

Brand_B::~Brand_B() {} 

int Brand_B::getEvent(void *rawData) { 


      .... readPort(......); 

} 

/* main **********************************************************/ 

int main(int argc, char **argv) { 

     Device *mydev; 

     char *buffer; 

    .............. 

    mydev->selectBrand(); 

    .......... 

    mydev->getEvent(buffer); 

    ........... 

} 
+0

Il y a beaucoup trop de sections '....' dans votre exemple. Ce que 'selectBrand' fait réellement n'est pas clair: qu'est ce que' X' et que voulez-vous dire par "utiliser la sous-classe"? – casablanca

+0

Si en "sélectionnant" vous projetez de baisser un objet pour "obtenir" une sous-classe, alors oui, votre approche est erronée. Je pense que fournir autant de code est déroutant - que diriez-vous juste les classes clés, les méthodes et comment vous voulez les utiliser? – belwood

+0

'~ Device()' devrait probablement être 'virtual'. – fredoverflow

Répondre

2

Ce n'est pas une bonne idée.

Généralement, la réponse est dynamic_cast, mais l'invocation d'un comportement spécifique des descendants d'une classe de base est généralement un mauvais signe de conception.

Vous pouvez essayer d'inverser la hiérarchie des classes et d'utiliser des modèles.

0

Il semblerait que vous essayiez d'implémenter quelque chose comme le polymorphisme lorsque C++ le fera pour vous. Si vous définissez des méthodes virtuelles dans votre classe de base et les remplacez dans vos sous-classes, les appels à ces méthodes sur un pointeur ou une référence au type de base doivent entraîner l'appel de l'implémentation de la sous-classe.

Par exemple:

class BaseClass 
{ 
    virtual void DoSomething() 
    { 
     printf("base"); 
    } 

}; 

class SubClass : public BaseClass 
{ 
    void DoSomething() 
    { 
     printf("sub"); 
    } 
}; 

int main() 
{ 
    BaseClass *myBase = new SubClass(); 
    myBase->DoSomething(); // should print "sub" to stdout 

    return 0; 
} 

Vous devez savoir ce dérivé de type (type de sous-classe) que vous souhaitez utiliser lorsque vous créez pour que l'instance a la fonctionnalité ajoutée du type dérivé. Si vous ne le faites pas, tout ce que vous obtenez est la fonctionnalité de la classe de base, et vous ne pouvez pas la traiter autrement que comme la classe de base (ou quelque chose de plus haut dans la hiérarchie d'héritage si votre classe de base hérite de quelque chose).

Vous pouvez même utiliser un membre pour différencier différentes instances si elles ne font rien de différent. Il est difficile de dire exactement à partir de l'exemple de code ce que vous voulez faire. Peut-être qu'un exemple plus précis de ce que vous essayez d'atteindre plutôt que de la façon dont vous essayez d'y parvenir serait utile.

+0

Mon problème est dans l'ensemble je ne sais pas quelle sous-classe je dois utiliser. C'est la demande d'une fonction dans la classe de base que j'ai appelée selectBrand. Sur la base de certaines vérifications, le code utilisera la sous-classe barnd_a ou brand_b. – Luke

+0

BaseClass * myBase = new SubClass(); serait parfait .... mais si j'ai plus de sous-classe à sélectionner? – Luke

+0

Après avoir lu votre deuxième message, je pense que je comprends un peu mieux le problème. Si vous essayez d'utiliser différentes implémentations de méthodes déclarées dans la classe de base, sans connaître celle que vous utilisez, vous voulez probablement le modèle de fabrique abstraite. Quelque chose comme: Device * périphérique = Devices :: CreateDevice(); où Device est une classe d'interface qui déclare les méthodes courantes des périphériques, et l'instance renvoyée est un périphérique spécifique déterminé par CreateDevice(). – ajmccluskey

0

s'il vous plaît, permettez-moi de reformuler le problème. J'ai 1 baseClass et quelques sous-classes; Brand_A .... Brand_N

Maintenant, dans le principal() je ne sais pas à l'avance quelle sous-classe je vais utiliser; cette sélection est demandée à une fonction de la classe de base que j'ai appelée selectBrand. Ce dont j'ai besoin, c'est d'un mécanisme pour sélectionner et utiliser la bonne sous-classe en fonction des conditions internes. Je veux masquer le principal() la sous-classe sélectionnée. Comment obtenir ceci?

1

Je pensais que je devrais étoffer le commentaire que j'ai fait ci-dessus. Tout d'abord, vous pouvez consulter la page Wikipedia pour plus d'informations sur le abstract factory pattern. Fondamentalement, il vous permet d'accéder à différentes implémentations d'une interface, avec l'implémentation utilisée déterminée lors de l'exécution. Cependant, vous ne savez toujours pas quelle implémentation vous obtenez car cela est décidé dans la méthode usine qui renvoie l'implémentation de l'interface. Par conséquent, vous pouvez uniquement utiliser les membres dans l'interface et non une implémentation spécifique. Un exemple qui utilise vos classes ci-dessus serait quelque chose comme:

class Device 
{ 
    virtual int getEvent(void *rawData) = 0; 
} 

class BrandA : public Device 
{ 
    // define constructors/destructors etc. 

    int getEvent(void *rawData) 
    { 
     // BrandA's implementation for getEvent 
    } 
} 

class BrandB : public Device 
{ 
    // define constructors/destructors etc. 

    int getEvent(void *rawData) 
    { 
     // BrandB's implementation for getEvent 
    } 
} 

class DeviceFactory 
{ 
    static Device *CreateDevice(/*any parameters for determining the device?*/) 
    { 
     // You probably don't want to randomly determine which implementation you use... 
     if ((rand() % 2) == 0) 
     { 
      return new BrandA(); 
     } 
     else 
     { 
      return new BrandB(); 
     } 
    } 
} 

int main() 
{ 
    // CreateDevice will decide which type of device we use, however we can only 
    // explicitly reference the members of the base class (Device). 
    Device *myDevice = DeviceFactory::CreateDevice(); 

    myDevice->getEvent(); 

    return 0; 
} 
+0

Oui - en supposant que j'ai compris les exigences de la question, c'est certainement la bonne façon de s'y prendre. En particulier, la classe de base ne sait rien sur les classes qui en héritent, ce qui est toujours un signe que les choses vont bien :-) – psmears

0

J'ai implémenté et testé ce code; ça fonctionne bien. Est-ce bon design ou peut être mieux fait?

class BehaviorBase 
{ 
public: 
    virtual ~BehaviorBase() {} 
    virtual void DoSomethingOn(Object* obj) {} 
}; 

class Object 
{ 
public: 
    BehaviorBase* behavior; 
    void DoSomething(); 
    void ChangeBehavior(int param); 
    ~Object(); 
} 

class BehaviorA: public BehaviorBase 
{ 
    void DoSomethingOn(Object* obj) 
    { 
    printf("Behavior A\n"); 
    } 
}; 

class BehaviorB: public BehaviorBase 
{ 
    string other_data; 
    void DoSomethingOn(Object* obj) 
    { 
    printf("Behavior B\n"); 
    } 
}; 

void Object::DoSomething() 
{ 
    behavior->DoSomethingOn(this); 
} 

Object::~Object() 
{ 
    delete behavior; 
} 

void Object::ChangeBehavior(int param) 
{ 
    delete behavior; 
    switch(param) 
    { 
     case 1: behavior = new BehaviorA; break; 
     case 2: behavior = new BehaviorB; break; 
    } 
}  

int main(int argc, char **argv) { 
    int param=1; 
    Object *obj; 
    obj= new Object; 

    obj->ChangeBehavior(param); 
    obj->DoSomething();  
    delete obj; 
    return(0); 
}