Полиморфный экземпляр-конструктор с преобразованием типов - PullRequest
0 голосов
/ 14 октября 2011

Мне нужно скопировать-построить объект, одновременно изменив его тип на другой класс, являющийся членом той же иерархии классов. Я читал о полиморфных копирующих конструкторах и (надеюсь) понял идею, стоящую за этим. Тем не менее, я до сих пор не знаю, применим ли этот шаблон к моему делу и, если да, то как его реализовать. Я думаю, что будет лучше, если я покажу, что мне нужно на примере.

Существует класс Base и два дочерних класса, Child1 и Child2. Мне нужно создать объект типа Child2 на основе Child1, т.е. больше всего мне нужно скопировать объект, на который указывает p_int, с Child1 до Child2. Я написал простую программу, чтобы проиллюстрировать это:

#include <iostream>
using namespace std;

class Base {
public:
    Base() { p_int = new int; *p_int = 0; }
    ~Base() { delete p_int; }
    virtual Base* clone() const = 0;

    void setpInt(int val) { *p_int = val; }
    void setInt(int val) { a = val; }
    virtual void print() {
        cout << "Base: ";
        cout << (long)p_int << ":" << *p_int << " " << a << endl;
    }
protected:
    int* p_int;
    int a;
};

class Child1 : public Base {
public:
    Child1() {};
    Child1(const Child1& child) {
        p_int = new int (*child.p_int);
        a = child.a + 1;
    }

    Base* clone() const { return new Child1(*this); }

    void print() {
        cout << "Child1: ";
        cout << (long)p_int << ":" << *p_int << " " << a << endl;
    }
};

class Child2 : public Base {
public:
    Child2() {};
    Child2(const Child2& child) {
        p_int = new int (*child.p_int);
        a = child.a + 1;
    }

    Base* clone() const { return new Child2(*this); }

    void print() {
        cout << "Child2: ";
        cout << (long)p_int << ":" << *p_int << " " << a << endl;
    }
};

int main() {
    Child1* c1 = new Child1();
    Child2* c2;

    c1->setpInt(4);
    c1->print();

    c2 = (Child2*)c1->clone();
    c2->print();
}

К сожалению, результат, как показано ниже, т.е. нет преобразования типа:

Child1: 162611224:4 0
Child1: 162611272:4 1

Что именно мне нужно реализовать, чтобы достичь того, что мне нужно? Я начинаю думать, что мне нужен механизм преобразования типов, а не полиморфный конструктор копирования, но я уже запутался.

РЕДАКТИРОВАТЬ: Спрашивается продолжение здесь

Ответы [ 4 ]

5 голосов
/ 14 октября 2011

Самое простое решение, вероятно, заключалось бы в реализации конструктора Child2, принимающего Child1& в качестве параметра.Тогда вы можете просто позвонить:

Child2* c2 = new Child2(*c1);
4 голосов
/ 14 октября 2011

Если у вас есть только 2 дочерних класса, тогда самый простой способ - создать конструктор преобразования:

class Child2: public Base
{
public: 
    Child2(Child1 const& child)
    {
            p_int = new int (*child.p_int);
            a = child.a + 1;        
    }
}; 

c2 = new Child2(*c1); 

Если у вас есть несколько дочерних классов, и вам нужно создать Child2 из любого из них,тогда вы могли бы сделать что-то вроде этого:

class Base
{
public: 
    void CopyFrom(Base* base)
    {
            p_int = new int (*base.p_int);
            a = base.a + 1;     
    }
}; 

class ChildX: public Base
{
public: 
    static ChildX* CreateFrom(Base* base)
    {
        ChildX ch = new ChildX(); 
        ch->CopyFrom(base); 
        return ch; 
    }
}; 

c2 = Child2::CreateFrom(c1); 
2 голосов
/ 14 октября 2011

Шаблоны clone () позволяют вам создать действительную копию / клон объекта дочернего класса, имеющего только базовую ссылку, например, в вашем случае это позволяет вам сделать следующее:

Base* basePtr = getBaseOrSomeDerivedObject();
Base* copy = basePtr.clone(); // Create a copy that is an object of an actual basePtr's type.

Вам может понадобиться «конструктор копирования», который позволяет копировать из базового класса, например ::100100

class Base {
public:
    // [...]    
    Base(const Base& other) : a(other.a + 1)
    {
        p_int = new int(*(other.p_int));
    }
    // [...]
};


class Child2 : public Base {
public:
    // [...]
    Child2(const Base& base) : Base(base) {}
    // [...]
};

int main() {
    // [...]
    c2 = new Child2(*c1);
    c2->print();
}

Результат:

Child1: 7275360:4 0
Child2: 7340936:4 1
2 голосов
/ 14 октября 2011
c2 = (Child2*)c1->clone();

Это серьезная ошибка, и приведение в стиле c скрывает ошибку .

Если вы используете приведение в стиле C ++, то ошибка не будет скрыватьи ты узнаешь это.В этом случае приведение в стиле C ++: dynamic_cast.Используйте его, чтобы самостоятельно обнаружить ошибку.

Как видно из кода, c1-clone() создает клон c1, тип которого Child1*, а clone() возвращает указатель типа Base*(после отката с Child1*), который вы пытаетесь понизить до Child2*.Приведение должно быть неудачным, если вы используете правильное приведение: dynamic_cast.

...