Может ли быть сделано создание копии без создания явной функции в чисто виртуальном базовом классе? - PullRequest
2 голосов
/ 28 сентября 2010

Моя цель - сделать глубокую копию класса, но виртуальный класс вызывает проблемы.

#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
}

Сообщение об ошибке компиляции:

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’

Я планирую получитьмножество других классов Handler (таких как Handler1, Handler2 и т. д.), которые наследуются от Vir и будут иметь своих уникальных членов (например, float a; или double b; и т. д.).Поэтому для меня нет смысла сохранять все функции getter и setter всех классов Handler в классе Vir.Я хочу сохранить свои методы получения и установки в классах Handler, потому что члены уникальны для классов Handler.Компилятор не позволяет мне сделать это.Помощь

Ответы [ 6 ]

2 голосов
/ 01 октября 2010

Может быть, я что-то упускаю, но разве вам не лучше с виртуальным clone методом на Vir?Это означает, что вы можете избежать неприятного приведения в конструкторе копирования ControlPanel, указанном в вашем собственном ответе.Это то же самое, что @Andrew Aylett предлагает в его ответ с использованием duplicate вместо clone.

Что-то вроде

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

, который реализованв Handler должно быть

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

Обратите внимание, что использование ковариантного возвращаемого типа, то есть Handler::clone может возвращать Handler*, а не просто Vir*, и при этом оставаться действительным переопределением Vir::clone.

Это делает ControlPanel конструктор копирования просто

ControlPanel( const ControlPanel& c )
    : v( c.v->clone() )
{
}
2 голосов
/ 30 сентября 2010

Добавьте функцию duplicate() в ваш абстрактный класс, который (в каждом производном классе) создает новый экземпляр с правильными значениями и возвращает его.В качестве альтернативы рассмотрим функцию copyFrom(Abs other), которая проверяет, что вы копируете из правильного типа, и, если это так, копирует поля.

В общем случае, если ваш класс ControlPanel имеет ссылку на объект Abs, он не должен пытаться выполнить свое дублирование путем проверки конкретного объекта-обработчика, он должен передавать дублирование виртуальной функции этого объекта.

0 голосов
/ 01 октября 2010

Как предположил Стив, я отвечаю на свой вопрос, потому что друг дал мне решение. Надеюсь, что это поможет любому, у кого есть вопрос, как сделать глубокую копию в C ++, где виртуальный класс может стать препятствием. Надеюсь, кто-нибудь найдет это полезным.

#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  */
0 голосов
/ 28 сентября 2010

Вы должны определить getI и setI как (чисто) виртуальные в Vir, чтобы сделать их доступными через подклассы.Обойти это невозможно.

0 голосов
/ 28 сентября 2010

Измените Vir *v на Handler *v; и посмотрите, компилируется ли ваш код.

Ваш класс Vir не объявляет / не определяет setI() и getI() функции-члены.

Или определить Vir как

class Vir//pure virtual class
{
    public:
    virtual void hi()=0;
    virtual int getI()const =0;
    virtual void setI(int)=0;
};
0 голосов
/ 28 сентября 2010

Почему компилятор позволяет вам?Эти методы не для этого интерфейса.

Вы можете использовать Factory Pattern для создания Vir, чтобы избежать необходимости добавления всех конструкторов в интерфейс Vir.Вам также следует рассмотреть возможность использования RAII , чтобы избежать функций стиля initialize ().

...