Сделайте копию неизвестного конкретного типа в c ++ - PullRequest
5 голосов
/ 27 января 2009

Предположим, у нас есть следующая иерархия классов:

class Base {
    ...
};

class Derived1 : public Base {
    ...
};

class Derived2 : public Base {
    ...
};

Учитывая Base*, который может указывать на объект Derived1 или Derived2, как я могу сделать копию реального объекта, учитывая, что его конкретный тип неизвестен. Я думал об определении конструкторов копирования, но я не думаю, что это возможно без знания реальных задействованных типов. Единственное решение, которое я могу придумать, - это определение метода clone() для каждого типа в иерархии. Кто-нибудь может придумать что-нибудь более изящное?

Ответы [ 7 ]

13 голосов
/ 27 января 2009

К сожалению, виртуальный шаблон клонирования / копирования - ваш единственный реальный выбор.

Есть варианты этого, но по сути все они сводятся к написанию функций для каждого типа, которые могут возвращать новую копию.

4 голосов
/ 27 января 2009

Вам необходимо реализовать метод виртуального клона для каждого объекта в иерархии. Как иначе ваш объект неопределенного типа узнает, как копировать себя?

Чтобы сделать это более очевидным для людей, пишущих производные классы, вы можете подумать о том, чтобы сделать метод клонирования абстрактным и в вашем базовом классе, чтобы заставить людей хотя бы что-то написать.

В нескольких местах для моего кода я также проверяю, чтобы убедиться, что метод to_string () реализован правильно для сериализации объекта в строку. Конкретный тест, который я использовал в своем классе для одновременного тестирования clone и to_string, выглядит следующим образом:

Base *obj1, *obj2;
# initialize obj1 in a reasonable manner
obj2 = obj1->clone();
assert( obj1->to_string() == obj2->to_string() );

Это тестирование метода clone () и сериализации объектов в строки (так что это не строго модульный тест), но это парадигма, которую просто обернуть в цикле вокруг всех объектов на фабрике, чтобы убедиться, что они следуют некоторому минимальному стандарту реализации clone () и to_string ().

Редактировать: Обновлен код с исправлением из комментария Страгера. Спасибо!

2 голосов
/ 27 января 2009

Не думаю, что это было бы легко сделать. Фактически, в Effective C ++ Мейерс предупредил, что если у вас есть функция, передающая значение, и скажем, что у вас есть

void foo(Base b)

и вы передаете Derived1 d1 в foo, копия будет обрезана - т.е. производные части не будут скопированы.

Но сейчас я цитирую по памяти ...

0 голосов
/ 27 января 2009

Вы правы насчет clone(). Вы не можете использовать конструкторы, потому что они вызывают статическое связывание. Вы не можете вернуть объект по значению, потому что это снова вызывает статическое связывание. Таким образом, вам нужна виртуальная функция, которая возвращает динамически размещенный объект.

0 голосов
/ 27 января 2009

Если вам нужен метод прозрачного клонирования, посмотрите на Неявный общий доступ .

0 голосов
/ 27 января 2009

Альтернативой является создание фабричного Base, при условии, что вы знаете все свои производные классы:

Если подумать, это, вероятно, плохая идея!

class Base {
    public:
        static Base *clone(const Base *obj) {
            if((Derived1 *o = dynamic_cast<Derived1 *>(obj)) {
                return new Derived1(*o);
            } else if((Derived2 *o = dynamic_cast<Derived2 *>(obj)) {
                return new Derived2(*o);
            }

            /* TODO When more classes are added, put them here.  (Probably bad design, yes.) */

            return new Base(obj);
        }

        Base *clone() const {
            return clone(this);
        }
};
0 голосов
/ 27 января 2009

Конструктор копирования невозможен (насколько я знаю), поскольку память выделяется за до вызова конструктора. Используется конструктор копирования известного типа, а не типа объекта.

Base *foo = new Derived1();
Base bar = *foo;               // Base's copy constructor is called.

Лучше всего было бы предложить метод clone(), как вы и предлагали.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...