2009-01-27 16 views
5

Załóżmy, że mamy następujące klasy Hierarchia:Wykonaj kopię nieznanego typu betonu w C++

class Base { 
    ... 
}; 

class Derived1 : public Base { 
    ... 
}; 

class Derived2 : public Base { 
    ... 
}; 

otrzymał Base* które mogłyby wskazywać albo Derived1 lub Derived2 przedmiot jak mogę zrobić kopię rzeczywista obiekt biorąc pod uwagę, że jego konkretny typ jest nieznany. Myślałem o zdefiniowaniu konstruktorów kopii, ale nie sądzę, aby było to możliwe bez znajomości faktycznych typów. Jedyne rozwiązanie, jakie mogę wymyślić to zdefiniowanie metody clone() dla każdego typu w hierarchii. Czy ktokolwiek może pomyśleć o czymś bardziej eleganckim?

Odpowiedz

12

Niestety wirtualny klon/kopia wzór jest jedynym prawdziwym wyborem.

Istnieją odmiany tego, ale zasadniczo wszystkie sprowadzają się do pisania funkcji dla każdego typu, które mogą zwrócić nową kopię.

2

Nie sądzę, że byłoby to łatwe. W rzeczywistości, w Effective C++, Meyers ostrzegł, że jeśli masz funkcji, które przechodzą przez wartość, i że masz

void foo(Base b) 

i mijamy Derived1 d1 do foo, kopia będzie plasterkach off - czyli części pochodne nie będą kopiowane.

Ale cytuję z pamięci w tej chwili ...

4

Musisz zastosować metodę wirtualnego klonowania na każdym obiekcie w hierarchii. Jak inaczej twój obiekt o nieokreślonym typie wiedziałby, jak się kopiować?

Aby uczynić to bardziej oczywistym dla osób piszących klasy pochodne, możesz pomyśleć o abstrakcji metody klonowania również w swojej klasie bazowej, aby zmusić ludzi do przynajmniej napisania czegoś.

W kilku miejscach dla mojego kodu, również testuję, aby upewnić się, że metoda to_string() jest poprawnie zaimplementowana do serializacji obiektu do łańcucha znaków. Specyficzny test, który użyłem w mojej klasie przetestować klon i to_string jednocześnie wygląda następująco:

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

ten testuje zarówno metodę clone() i przedmiot serializacji do strun (więc nie jest ściśle testów jednostkowych), ale jest to paradygmat, który można zawijać w pętlę wokół wszystkich obiektów w fabryce, aby upewnić się, że przestrzegają minimalnego standardu implementacji clone() i to_string().

Edycja: Zaktualizowany kod z poprawką z komentarza programu uruchamiającego. Dzięki!

+0

Myślę, że masz na myśli Base * obj1, * obj2 ;. =] – strager

+0

Dobry chwytacz! –

0

Konstruktor kopiowania nie jest możliwy (o ile wiem), ponieważ pamięć jest przydzielana przed konstruktor jest wywoływany. Używany jest konstruktor kopiowania znanego typu, a nie typu obiektu.

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

Najlepiej byłoby podać metodę clone(), zgodnie z sugestią.

-1

Alternatywą jest stworzenie fabrycznie jak Base, zakładając, że znasz wszystkich klas pochodnych:

Na drugim myśli, to chyba zły pomysł!

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); 
     } 
}; 
+1

Zabawne - po co to pisać, jeśli to taki zły pomysł? – Tom

+0

@ Tom: Cóż, to jest/prawdopodobnie/zły pomysł. Jestem pewien, że są rzadkie przypadki, w których można go użyć ... – strager

+0

Jest to * na pewno * zły pomysł. Klasa podstawowa powinna * nigdy * posiadać wiedzę o swoich dzieciach --- jest to jedno z najgorszych naruszeń zasady inwersji zależności. –

0

Masz rację co do clone(). Nie można używać konstruktorów, ponieważ wymuszają one wiązanie statyczne. Nie można zwrócić obiektu według wartości, ponieważ to znowu wymusza statyczne powiązanie. W związku z tym potrzebna jest funkcja wirtualna, która zwraca obiekt przydzielany dynamicznie.

Powiązane problemy