construction Cant copie se faire sans créer une fonction explicite dans la classe de base virtuelle pure?
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?
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 */