2009-04-23 5 views
11

Я недавно обнаружил, что когда у меня есть указатели внутри класса, мне нужно указать конструктор Copy.Копировальный конструктор с указателями

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

Я пытаюсь скопировать только значение из указателя скопированного объекта, но избегая назначения того же адреса.

Итак, что здесь не так?

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

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

Кроме того, реальный класс, который мне нужно реализовать, имеет 10 указателей, и он может меняться со временем. Не существует несколько умнее способ иметь глубокий конструктор копирования в C++? ...

ответ

12

С утверждением int* pointer вы только что определили указатель, но не выделено какой-либо памяти. Сначала вы должны указать на правильное расположение памяти, выделив некоторую память следующим образом: int* pointer = new int. Затем в конструкторе копирования вы должны выделить память для скопированного объекта. Кроме того, не забудьте освободить память, используя delete в деструкторе.

Я надеюсь, что этот пример поможет:

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

Не забудьте удалить m_p перед тем, как назначить для него новый объект. – xtofl

+0

Я не определил оператор присваивания, это просто конструктор копирования. Следовательно, нет необходимости удалять m_p. – Naveen

+3

В конструкторе m_p изначально будет неопределенным, если вы явно не поместите его в список инициализаторов. Если вы попытаетесь удалить неопределенный указатель, произойдут плохие вещи. –

1

, если у него есть указатель на обычный тип, то

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

, если у него есть указатель на какой-то базовый класс затем

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

Clone является реализация prototype pattern

не забудьте удалить указатель в деструкторе

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

Чтобы исправить ваш пример

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

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

Ваш пример не очень ясен, он не показывает реализацию вашего конструктора копирования или как инициализируется член pointer.

1

Ваша проблема в этой линии здесь:

*pointer = a; 

Весь материал, который обычно происходит в конструкторе по умолчанию пока не произошло, в том числе выделение памяти для *pointer.

Исправление состоит в том, чтобы выделить память для целого числа.Вы можете использовать malloc и друзей или new для этого, но убедитесь, что это тот же метод, который вы используете в своем конструкторе по умолчанию, потому что вы получаете только один деструктор, а вызовы должны совпадать.

1

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

0

При написании Copy Constructor вы должны выделить память для всех членов. В вашем случае:

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

Оператор = как-то похоже, но без выделения памяти.

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

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

Это не совсем так. Когда у вас есть указатели в вашем классе и выделяете память с помощью new, вам придется беспокоиться о конструкторе копирования. Кроме того, не забудьте оператор присваивания и деструктор. Вы должны удалить выделенную память, используя delete.

Это называется Law Of The Big Three.

Пример:

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

Недавно я обнаружил, что, когда я есть указатели в классе, мне нужно указать конструктор копирования

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

+0

Зачем? Является ли копирование чего-либо концептуально «запрещенным» в программировании, или это потому, что копирование может привести к проблемам на языках OO? – Biga

+0

Это не «запрещено» - это просто не имеет смысла и/или дорого. –

0

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

Например, возможно, что int * member должен быть std :: vector.

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

Только если ни одно из указанных выше не является выполнимым, если вы реализуете свой собственный конструктор копий или оператор присваивания.