construction Cant copie se faire sans créer une fonction explicite dans la classe de base virtuelle pure?

StackOverflow https://stackoverflow.com/questions/3813964

Question

Mon objectif est de faire une copie en profondeur d'une classe, mais une classe virtuelle est à l'origine des problèmes.

#include<iostream>
using namespace std;

class Vir//pure virtual class
{
    public:
    virtual void hi()=0;
};

class Handler:public Vir
{
    public:
    int i;
    Handler() {}
    Handler(int val):i(val) {}
    void hi() {cout<<"Value of i="<<i<<endl;}
    int getI() const {return i;}
    void setI(int j) {i=j;}
};

class ControlPanel
{
    public:
    Vir *v;
    ControlPanel(const ControlPanel& c)//copy constructor
    {
        v=new Handler;
        v->setI(c.getI());
    }
    int getI()const {return v->getI();}

    void initialize() {v=new Handler(10);}
    void hi() {v->hi();}
    ControlPanel() {}
    ~ControlPanel() {delete v;}
};

int main()
{
    ControlPanel cc;
    cc.initialize();
    cc.hi();
    ControlPanel bb(cc);//copying cc into bb
}

Le message d'erreur de compilation:

test.cpp: In copy constructor ‘ControlPanel::ControlPanel(const ControlPanel&)’:
test.cpp:28: error: ‘class Vir’ has no member named ‘setI’
test.cpp: In member function ‘int ControlPanel::getI() const’:
test.cpp:30: error: ‘class Vir’ has no member named ‘getI’

Je prévois d'avoir beaucoup plus de classes de gestionnaire (comme Handler1, Handler2 etc) qui hérite de Vir et auront leurs propres membres uniques (comme flotter un, ou double b, etc.). Donc, il n'a pas de sens pour moi de garder toutes les fonctions de lecture et setter de toutes les classes de gestionnaire dans la classe Vir. Je veux garder mes méthodes getter et setter dans les classes de gestionnaire parce que les membres sont uniques aux classes de gestionnaire. Le compilateur ne me permet de le faire. Aide?

Était-ce utile?

La solution

Peut-être que je manque quelque chose, mais seriez-vous pas mieux avec une méthode de clone virtuelle sur Vir? Cela signifie que vous pouvez éviter le casting méchant dans le constructeur de copie ControlPanel décrit dans votre propre réponse. Ceci est le même que suggère dans Aylett @ Andrew sa réponse avec duplicate utilisé au lieu de clone.

Quelque chose comme

class Vir
{
    public:
    virtual Vir* clone() const = 0;
    ...
};

qui est mis en oeuvre dans Handler être

Handler* Handler::clone() const
{
    return new Handler( *this );
}

Notez l'utilisation du type de retour covariant à savoir Handler::clone est autorisé à retourner un Handler* plutôt que juste un Vir* et encore une dérogation valable Vir::clone.

Cela rend le constructeur de copie ControlPanel simplement

ControlPanel( const ControlPanel& c )
    : v( c.v->clone() )
{
}

Autres conseils

Ajoutez une fonction duplicate() à votre classe abstraite, qui (dans chaque classe dérivée) crée une nouvelle instance avec les bonnes valeurs et le renvoie. Vous pouvez également envisager une fonction copyFrom(Abs other) qui vérifie que vous copiez du type correct et si oui, copie les champs sur.

En général, si votre classe ControlPanel a une référence à un objet Abs, il ne faut pas essayer de faire son double emploi en inspectant le béton objet gestionnaire, il devrait être passer la duplication hors d'une fonction virtuelle sur cet objet.

Pourquoi le compilateur vous permettre? Ces méthodes ne sont pas sur cette interface.

Vous pouvez utiliser le pour créer votre Vir, pour éviter d'avoir à ajouter tous les constructeurs à l'interface de Vir. Vous devriez également envisager d'utiliser RAII pour éviter les fonctions de style initialize ().

Changement Vir *v à Handler *v; et voir si votre code est compilé ou non.

Votre Vir de classe ne déclare pas / définir les fonctions membres setI() et de getI().

Ou définir Vir comme

class Vir//pure virtual class
{
    public:
    virtual void hi()=0;
    virtual int getI()const =0;
    virtual void setI(int)=0;
};

Vous devez définir getI et setI comme (pur) virtuel Vir pour les rendre accessibles par l'intermédiaire de sous-classes. Pas moyen de contourner cela.

Comme Steve a suggéré, je réponds à ma propre question coz un ami m'a donné une solution. Espérons que cela serait utile à toute personne qui a la question de savoir comment faire une copie profonde en C ++ où une classe virtuelle peut être un barrage routier. L'espoir que quelqu'un trouve cela utile.

#include<iostream>
using namespace std;

class Vir//pure virtual class
{
    public:
    virtual void hi()=0;
    virtual int getI() {std::cout << "Inside Base class" << std::endl;}
    virtual void setI(int i) {cout<<"In base"<<endl;}
    virtual int getX() {}
    virtual void setX(int x) {}
};
class Model
{
    public:
    int x;
    Model(const Model& mm) {x=mm.x;}
    Model():x(555) {cout<<"Model constructor called"<<endl;}
    int getX() {return x;}
    void setX(int xx) {x=xx;}
};

class Handler:public Vir
{
    public:
    int i;
    Model *m;

    Handler() {m=new Model;cout<<"Handler constructor called"<<endl;}
    Handler(const Handler& h)
    {
    std::cout << "Inside Handler @lineNumber:" << __LINE__ << std::endl;
    i=h.i;
    m=new Model(*h.m);
    }
    Handler(int val):i(val) {}
    ~Handler() {delete m;}
    void hi() {cout<<"Value of i="<<i<<endl;}
    int getI() {return i;}
    void setI(int j) {i=j;}
    int getX() {return m->getX();}
    void setX(int xx) {m->setX(xx);}
};

class ControlPanel
{
    public:
    int abc;
    Vir *v;
    ControlPanel(const ControlPanel& c)//copy constructor
    {
    std::cout << "Inside ControlPanel @lineNumber:" << __LINE__ << std::endl;
        v=new Handler((Handler&)*(c.v));
    }
    void initialize() {v=new Handler();v->setI(10);abc=222;}
    void hi() {v->hi();}
    ControlPanel() {}
    ~ControlPanel() {delete v;}
};

int main()
{
    ControlPanel cc;
    cc.initialize();
    cc.hi();    
    cout << "(cc.v)->i::" << (cc.v)->getI() << endl;
    cout<<"x value cc="<<(cc.v)->getX()<<endl;
    ControlPanel bb(cc);//copying cc into bb
    cout << "(bb.v)->i::" << (bb.v)->getI() << endl;
    cout<<"x value bb="<<(bb.v)->getX()<<endl;
    (cc.v)->setI(999);
    (cc.v)->setX(888888888);
    cout << "(cc.v)->i::" << (cc.v)->getI() << endl;
    cout << "(bb.v)->i::" << (bb.v)->getI() << endl;
    cout<<"x value cc="<<(cc.v)->getX()<<endl;
    cout<<"x value bb="<<(bb.v)->getX()<<endl;
}//main
/*
Output:
Model constructor called
Handler constructor called
Value of i=10
(cc.v)->i::10
x value cc=555
Inside ControlPanel @lineNumber:52
Inside Handler @lineNumber:32
(bb.v)->i::10
x value bb=555
(cc.v)->i::999
(bb.v)->i::10
x value cc=888888888
x value bb=555  */
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top