2009-04-23 24 views
11

Niedawno odkryłem, że gdy mam wskaźniki w klasie, muszę określić konstruktora Kopiuj.Kopiuj konstruktora ze wskaźnikami

Aby się tego nauczyć, wykonałem poniższy prosty kod. Kompiluje, ale daje mi błąd wykonania podczas wykonywania konstruktora kopiowania.

Próbuję skopiować tylko wartość ze wskaźnika skopiowanego obiektu, ale unikaję przypisywania tego samego adresu.

Co jest nie tak?

class TRY{ 
     public: 
     TRY(); 
    ~TRY(); 
     TRY(TRY const &); 

     int *pointer; 

     void setPointer(int); 
    }; 


    void TRY::setPointer(int a){ 
     *pointer = a; 

     return; 
    } 


    TRY::TRY(){} 


    TRY::~TRY(){} 


    TRY::TRY(TRY const & copyTRY){ 
     int a = *copyTRY.pointer; 
     *pointer = a; 
    } 



    int main(){ 

     TRY a; 
     a.setPointer(5); 

     TRY b = a; 

     b.setPointer(8); 

     cout << "Address of object a = " << &a << endl; 
     cout << "Address of object b = " << &b << endl; 

     cout << "Address of a.pointer = " << a.pointer << endl; 
     cout << "Address of b.pointer = " << b.pointer << endl; 

     cout << "Value in a.pointer = " << *a.pointer << endl; 
     cout << "Value in b.pointer = " << *b.pointer << endl; 

     return 0; 
    } 

będę stosując tę ​​koncepcję dla innych klas z dużą ilością wskazówek w nim, gdzie należy skopiować wszystkie wartości z dnia obiektu do drugiego. Kopiowanie jest początkowo niezbędne dla tego kodu, więc chciałbym zachować możliwość kopiowania (nie będę ukrywał konstruktora kopii jako prywatnego).

Poza tym prawdziwa klasa, którą muszę wprowadzić, ma 10 wskaźników i może się zmieniać z czasem. Nie jest tam nieco mądrzejszy sposób na głęboki konstruktor kopiujący w C++? ...

Odpowiedz

12

ze stwierdzeniem int* pointer właśnie zdefiniowany wskaźnik, ale nie przydzielonej żadnej pamięci. Najpierw należy wskazać właściwą lokalizację w pamięci, przydzielając trochę pamięci w następujący sposób: int* pointer = new int. Następnie ponownie w konstruktorze kopiowania musisz przydzielić pamięć dla kopiowanego obiektu. Nie zapomnij też zwolnić pamięci za pomocą polecenia delete in the destructor.

Mam nadzieję, że ten przykład pomaga:

class B 
{ 

public: 
    B(); 
    B(const B& b); 
    ~B(); 
    void setVal(int val); 

private: 
    int* m_p; 
}; 

B::B() 
{ 
    //Allocate the memory to hold an int 
    m_p = new int; 

    *m_p = 0; 
} 

B::B(const B& b) 
{ 
    //Allocate the memory first 
    m_p = new int; 

    //Then copy the value from the passed object 
    *m_p = *b.m_p; 
} 

B::~B() 
{ 

    //Release the memory allocated 
    delete m_p; 
    m_p = NULL; 
} 

void B::setVal(int val) 
{ 
    *m_p = val; 
} 
+3

Nie zapomnij usunąć m_p przed przydzieleniem mu nowego obiektu. – xtofl

+0

Nie zdefiniowałem operatora przypisania, to tylko konstruktor kopii. Dlatego nie ma potrzeby usuwania m_p. – Naveen

+3

W konstruktorze, m_p początkowo będzie niezdefiniowane, dopóki nie zostanie jawnie umieszczone na liście inicjalizacyjnej. Jeśli spróbujesz usunąć niezdefiniowany wskaźnik, wystąpią złe rzeczy. –

1

jeśli ma wskaźnik do regularnego typu następnie

A::A(const A& a): 
    pointer_(new int(*a.pointer_)) 
{ 
} 

jeśli ma wskaźnik do jakiejś klasy bazowej następnie

A::A(const &a): 
    pointer_(a.pointer_->clone()) 
{ 
} 

Clone jest realizacja prototype pattern

nie zapomnij usunąć wskaźnik w destructor

A::~A() 
{ 
    delete pointer_; 
} 

naprawić przykład

TRY::TRY(TRY const & copyTRY){ 
    int a = *copyTRY.pointer; 
    pointer = new int(a); 
} 
3

Jeśli chcesz zrobić głęboką kopię, należy oczywiście także przydzielić nową pamięć do przechowywania wartości. Jeśli oryginał ma wskaźnik do int i nie chcesz, aby ta kopia korzystała z tej samej wartości wskaźnika, musisz przydzielić nową pamięć do przechowywania int, a następnie skopiować tam wartość.

Twój przykład nie jest zbyt jasny, nie pokazuje implementacji twojego konstruktora kopiowania ani sposobu, w jaki inicjowany jest członek pointer.

1

Twój problem jest w tym wierszu tutaj:

*pointer = a; 

wszystkie rzeczy, które zwykle dzieje się w domyślnej konstruktora nie zdarzyło się jeszcze, w tym alokacji pamięci dla *pointer.

Rozwiązaniem jest przydzielenie pamięci dla liczby całkowitej.Możesz użyć do tego celu malloc i znajomych lub new, ale upewnij się, że jest to ta sama metoda, której używasz w domyślnym konstruktorze, ponieważ dostajesz tylko jeden destruktor i połączenia muszą być zgodne.

1

Jeśli dobra płyta (płytka) jest w porządku, to nie musisz nic robić. Jeśli chcesz uzyskać głęboką kopię, musisz przydzielić nowe miejsce na kopie wszystkich członków.

0

Podczas pisania Copy Constructor należy przydzielić pamięć dla wszystkich członków. W twoim przypadku:

TRY::TRY(TRY const & copyTRY){ 
    pointer = new int(*(copyTry.pointer)); 
} 

Operator = jest jakoś podobnie, ale bez alokacji pamięci.

TRY& operator=(TRY const& otherTRY){ 
     this->a = *(otherTry.pointer) 
     return *this 
} 
8

Niedawno odkryto, że gdy mam wskaźniki w klasie, muszę określić konstruktor kopiujący.

To nie jest do końca prawdą. Kiedy masz wskaźniki w swojej klasie i przydzielasz pamięć za pomocą new, musisz się martwić konstruktorem kopiowania. Nie zapomnij również o operatorze przypisania i destruktorze. Musisz usunąć pamięć przydzieloną za pomocą delete.

Nazywa się Law Of The Big Three.

Przykład:

~Matrix(); //Destructor 
    Matrix(const Matrix& m); //Copy constructor 
    Matrix& operator= (const Matrix& m); //Assignment operator 
1

Niedawno odkryto, że gdy mam wskaźniki w klasie, muszę określić konstruktor kopiujący

Nie częściej niż jest to dobrym pomysłem jest po prostu wyłączyć go, deklarując go (i operatora przypisania) jako prywatny, a nie go wdrażając.

+0

Dlaczego? Czy kopiowanie czegoś koncepcyjnie "zabronionego" w programowaniu, czy też dlatego, że kopiowanie może powodować problemy w językach OO? – Biga

+0

Nie jest "zabronione" - często nie ma sensu i/lub jest kosztowne. –

0

Najczęściej, jeśli TY chcesz napisać konstruktora kopii lub operatora przypisania, robisz coś nie tak. Pozostaw konstruktory kopiowania i operatory przypisania do implementatorów standardowej biblioteki. Skomponuj klasy, które już można kopiować i przyporządkowywać, a nie będziesz musiał pisać własnych.

Na przykład, może to, że członek int * powinien być zamiast tego std :: vector.

Jeśli nie możesz uczynić klasy domyślną kopiowalną/możliwą do przypisania, możesz ją uczynić niemożliwą do kopiowania/przyporządkowania przez deklarowanie, ale nie zaimplementowanie prywatnego konstruktora kopii i operatora przypisania.

Tylko jeśli żaden z powyższych nie jest możliwy, jeśli wdrożysz własnego konstruktora kopiowania lub operatora przypisania.

Powiązane problemy