2009-04-23 40 views
11

Gần đây tôi đã phát hiện ra rằng khi tôi có con trỏ trong một lớp, tôi cần phải chỉ định một hàm tạo Sao chép.Copy constructor với con trỏ

Để tìm hiểu điều đó, tôi đã thực hiện mã đơn giản sau đây. Nó biên dịch, nhưng cung cấp cho tôi lỗi thời gian chạy khi thực hiện hàm tạo bản sao.

Tôi đang cố gắng sao chép chỉ giá trị từ con trỏ của đối tượng đã sao chép, nhưng tránh chỉ định cùng một địa chỉ.

Vì vậy, có gì sai ở đây?

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

Tôi sẽ sử dụng khái niệm này cho các lớp khác có nhiều con trỏ trong đó, nơi tôi cần sao chép tất cả các giá trị từ đối tượng này sang đối tượng kia. Việc sao chép ban đầu là cần thiết cho mã này, vì vậy tôi muốn giữ khả năng sao chép (tôi sẽ không ẩn phần khởi tạo sao chép dưới dạng riêng tư).

Bên cạnh đó, lớp thực sự tôi cần triển khai có 10 con trỏ và có thể thay đổi theo thời gian. không có cách nào thông minh hơn để có một constructor sao chép sâu trong C++? ...

Trả lời

12

Với tuyên bố int* pointer bạn vừa định nghĩa một con trỏ nhưng chưa phân bổ bất kỳ bộ nhớ. Trước tiên, bạn nên làm cho nó trỏ đến một vị trí bộ nhớ thích hợp bằng cách phân bổ một số bộ nhớ như thế này: int* pointer = new int. Sau đó, trong constructor sao chép một lần nữa, bạn phải cấp phát bộ nhớ cho đối tượng đã sao chép. Ngoài ra, đừng quên để giải phóng bộ nhớ bằng cách sử dụng xóa trong destructor.

Tôi hy vọng ví dụ này giúp:

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

Đừng quên xóa m_p trước khi phân bổ một đối tượng mới cho nó. – xtofl

+0

Tôi chưa xác định toán tử gán, nó chỉ là một hàm tạo bản sao. Do đó không cần xóa m_p. – Naveen

+3

Trong một hàm tạo, m_p ban đầu sẽ không được xác định trừ khi bạn đặt nó vào danh sách khởi tạo một cách rõ ràng. Nếu bạn cố gắng xóa một con trỏ không xác định, những điều xấu sẽ xảy ra. –

1

nếu nó có một con trỏ tới một kiểu bình thường sau đó

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

nếu nó có một con trỏ đến một số lớp cơ sở sau đó

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

Clone là một thi hành một prototype pattern

Đừng quên để xóa các con trỏ trong destructor

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

Để khắc phục ví dụ của bạn

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

Nếu bạn muốn làm một bản sao sâu, bạn tất nhiên cũng phải cấp phát bộ nhớ mới để giữ các giá trị. Nếu bản gốc có một con trỏ đến một int, và bạn không muốn bản sao sử dụng cùng một giá trị con trỏ, bạn phải cấp phát bộ nhớ mới để giữ một int, và sau đó sao chép giá trị ở đó.

Ví dụ của bạn không rõ ràng, nó không hiển thị việc triển khai hàm tạo bản sao của bạn hoặc cách thành viên pointer được khởi tạo.

1

Vấn đề của bạn là ở dòng này ngay tại đây:

*pointer = a; 

Tất cả những thứ mà thường xảy ra trong constructor mặc định của bạn chưa xảy ra, bao gồm cả việc phân bổ bộ nhớ cho *pointer.

Khắc phục là cấp phát bộ nhớ cho một số nguyên.Bạn có thể sử dụng malloc và bạn bè hoặc new cho điều này, nhưng hãy đảm bảo đó là phương pháp tương tự bạn sử dụng trong hàm dựng mặc định của mình, bởi vì bạn chỉ nhận được một hàm hủy và các cuộc gọi phải khớp.

1

Nếu bản sao thành viên khôn ngoan (nông) không sao, thì bạn không phải làm gì cả. Nếu bạn muốn có một bản sao sâu, bạn phải cấp phát bộ nhớ mới cho các bản sao của tất cả các thành viên.

0

Khi viết một Trình tạo bản sao, bạn nên cấp phát bộ nhớ cho tất cả các thành viên. Trong trường hợp của bạn:

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

Toán tử = bằng cách nào đó tương tự, nhưng không có cấp phát bộ nhớ.

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

Gần đây tôi đã phát hiện ra rằng khi tôi có con trỏ trong một lớp học, tôi cần để xác định một constructor Copy.

Điều này không hoàn toàn đúng. Khi bạn có con trỏ trong lớp của bạn và phân bổ bộ nhớ bằng cách sử dụng new thì bạn phải lo lắng về việc tạo bản sao. Ngoài ra, đừng quên toán tử gán và hàm hủy. Bạn phải xóa bộ nhớ được cấp phát bằng cách sử dụng delete.

Nó được gọi là Law Of The Big Three.

Ví dụ:

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

Gần đây tôi đã phát hiện ra rằng khi tôi có con trỏ trong một lớp học, tôi cần để xác định một constructor sao chép

Thường xuyên hơn không nó là một ý tưởng tốt để chỉ đơn giản là vô hiệu hóa nó bằng cách tuyên bố nó (và các nhà điều hành assigment) tư nhân và không thực hiện nó.

+0

Tại sao lại như vậy? Đang sao chép một cái gì đó khái niệm "bị cấm" trong lập trình, hay là vì sao chép có thể dẫn đến rắc rối trong các ngôn ngữ OO? – Biga

+0

Nó không phải là "cấm" - nó chỉ thường không có ý nghĩa và/hoặc là tốn kém. –

0

Thường xuyên hơn không, nếu bạn cần viết một hàm tạo bản sao hoặc toán tử gán bạn đang làm gì đó sai. Để các nhà xây dựng bản sao và các toán tử gán cho người triển khai của thư viện chuẩn. Soạn các lớp của bạn về các phần tử đã sao chép và có thể gán và bạn sẽ không phải viết các phần tử của riêng mình.

Ví dụ: có thể thành viên int * đó phải là std :: vector thay thế.

Nếu bạn không thể đặt lớp mặc định có thể sao chép/chuyển nhượng được, có thể bạn không thể sao chép/gán được bằng cách khai báo, nhưng không triển khai, trình tạo bản sao riêng và toán tử gán.

Chỉ khi không có điều nào ở trên là khả thi nếu bạn triển khai trình tạo bản sao của riêng bạn hoặc toán tử gán.

Các vấn đề liên quan