2008-10-19 38 views
5

Giả sử tôi có cấu trúc "s" với biến thành viên trỏ int "i". Tôi phân bổ bộ nhớ trên heap cho i trong constructor mặc định của s. Sau đó trong một số phần khác của mã tôi vượt qua một thể hiện của s theo giá trị cho một số chức năng. Tôi đang làm một bản sao nông ở đây? Giả sử tôi đã không thực hiện bất kỳ nhà xây dựng bản sao hoặc nhà khai thác gán hoặc bất cứ điều gì cho s ... chỉ là constructor mặc định.Câu hỏi về bản sao nông trong C++

Trả lời

8

Để theo dõi những gì @ [don.neufeld.myopenid.com] cho biết, nó không chỉ là bản sao nông, nhưng nó hoặc là (lấy của bạn) một rò rỉ bộ nhớ hoặc một con trỏ lơ lửng.

// memory leak (note that the pointer is never deleted) 
class A 
{ 
    B *_b; 
    public: 
    A() 
    : _b(new B) 
    { 
    } 
}; 

// dangling ptr (who deletes the instance?) 
class A 
{ 
    B *_b; 
    public: 
    A() 
    ... (same as above) 

    ~A() 
    { 
    delete _b; 
    } 
}; 

Để giải quyết vấn đề này, có một số phương pháp.

Luôn triển khai trình tạo bản sao và toán tử = trong các lớp sử dụng con trỏ bộ nhớ thô.

class A 
{ 
    B *_b; 
    public: 
    A() 
    ... (same as above) 

    ~A() 
    ... 

    A(const A &rhs) 
    : _b(new B(rhs._b)) 
    { 
    } 

    A &operator=(const A &rhs) 
    { 
    B *b=new B(rhs._b); 
    delete _b; 
    _b=b; 
    return *this; 
}; 

Không cần phải nói, đây là một nỗi đau lớn và có khá ít sự tinh tế để có được quyền. Tôi thậm chí không hoàn toàn chắc chắn rằng tôi đã làm nó ngay tại đây và tôi đã thực hiện nó một vài lần. Đừng quên bạn phải sao chép tất cả các thành viên - nếu bạn thêm một số thành viên mới sau này, đừng quên thêm chúng vào!

Tạo hàm tạo bản sao và toán tử = private trong lớp của bạn. Đây là giải pháp "khóa cửa". Nó đơn giản và hiệu quả, nhưng đôi khi quá bảo vệ.

class A : public boost::noncopyable 
{ 
    ... 
}; 

Không sử dụng con trỏ thô. Điều này rất đơn giản và hiệu quả. Có rất nhiều lựa chọn ở đây:

  • lớp Sử dụng chuỗi thay vì con trỏ char thô
  • Sử dụng std :: auto_ptr, đẩy mạnh :: shared_ptr, đẩy mạnh :: scoped_ptr vv

Ví dụ:

// uses shared_ptr - note that you don't need a copy constructor or op= - 
// shared_ptr uses reference counting so the _b instance is shared and only 
// deleted when the last reference is gone - admire the simplicity! 
// it is almost exactly the same as the "memory leak" version, but there is no leak 
class A 
{ 
    boost::shared_ptr<B> _b; 
    public: 
    A() 
    : _b(new B) 
    { 
    } 
}; 
+0

Toán tử gán của bạn không phải là ngoại lệ an toàn. Xem câu hỏi gần đây về an toàn bản sao và ngoại lệ: http://stackoverflow.com/questions/214891/checklist-for-writing-copy-constuctor-and-assignment-operator-in-c#214966 –

+0

Doh, ok tôi đã khắc phục điều đó . Bạn thấy những gì tôi có ý nghĩa về nó là một nỗi đau và khó khăn để có được ngay sau đó huh –

+0

Cách tốt nhất để làm cho các nhà xây dựng bản sao và nhà điều hành = tư nhân là để kế thừa từ boost :: noncopyable. Bạn nên làm điều đó cho mỗi lớp duy nhất trừ khi bạn biết chắc chắn nó sẽ có thể sao chép được. – CesarB

5

Vâng, đó là bản sao nông. Bây giờ bạn có hai bản sao của s (một trong những người gọi, một trên stack như một tham số), mỗi trong đó có chứa một con trỏ đến cùng một khối bộ nhớ.

2

Bạn sẽ có hai bản sao của s struct, mỗi trong số đó sẽ có i con trỏ của riêng mình, nhưng cả hai i con trỏ sẽ có cùng giá trị trỏ đến cùng một địa chỉ trong bộ nhớ - vì vậy có, nó sẽ là một bản sao cạn .