2012-01-29 27 views
11

Từ những gì tôi hiểu tôi có thể "vô hiệu hóa" sao chép và gán cho đối tượng của tôi bằng cách định nghĩa constructor sao chép tin và toán tử gán:Không cho phép các nhiệm vụ và đi ngang qua giá trị

class MyClass 
{ 
private: 
    MyClass(const MyClass& srcMyClass); 
    MyClass& operator=(const MyClass& srcMyClass); 
} 

Nhưng việc sử dụng này là gì?
Đây có phải là hành vi không tốt?

Tôi sẽ đánh giá cao nếu bạn có thể mô tả tình huống, trong đó nó sẽ là hợp lý/hữu ích để "vô hiệu hóa" gán và sao chép nhà xây dựng theo cách này.

+1

Một singleton sẽ là một ví dụ. –

Trả lời

11

Sẽ hữu ích khi không sao chép đối tượng của bạn. Nó chắc chắn không được coi là thực hành xấu.

Ví dụ, nếu bạn có một lớp đại diện cho một kết nối mạng, nó không có ý nghĩa để sao chép đối tượng đó. Một lần khác bạn có thể muốn một lớp không thể sao chép được nếu bạn có một lớp đại diện cho một người chơi trong một trò chơi nhiều người chơi. Cả hai lớp này đại diện cho những thứ không thể được sao chép trong thế giới thực, hoặc không có ý nghĩa để sao chép (một người, một kết nối).

Ngoài ra, nếu bạn đang cố triển khai Singleton, đó là quy trình chuẩn để làm cho các đối tượng không thể sao chép được.

+0

Cảm ơn, nó có ý nghĩa. Điều gì về nhà điều hành chuyển nhượng? – LihO

+3

@LihO nói chung nếu bạn vô hiệu hóa một trong những bạn vô hiệu hóa khác. Nếu bạn không vô hiệu hóa toán tử gán, bạn có thể làm điều này: 'MyClass a, b; a = b; 'và nếu bạn không vô hiệu hóa hàm tạo bản sao, bạn có thể thực hiện' MyClass b; MyClass a (b); 'Vì vậy, nếu bạn không vô hiệu hóa cả hai, bạn có thể nhận được xung quanh một trong những khác bị vô hiệu hóa. –

+0

Vâng, giờ tôi đã hiểu. – LihO

0

Khi bạn đang cố gắng triển khai một mẫu đơn, nó hoàn toàn có thể chấp nhận để sử dụng một hàm tạo riêng vì nó có nghĩa là chỉ được khởi tạo bên trong chính nó và từ đâu khác. Khi được gọi, hàm tạo không thể bị thu hồi. Vì vậy, hàm tạo được gọi chỉ sau khi kiểm tra nếu điều kiện singleton thỏa mãn.

2

Đó là một thực tế khá phổ biến. Có rất nhiều ví dụ khi sao chép không phù hợp.

Giả sử đối tượng của bạn đại diện cho một ổ cắm phía máy chủ mở (tức là kết nối mạng đến); ngữ nghĩa của việc tạo ra một bản sao của đối tượng đó là gì?

5

Nói chung mọi lớp học quản lý tài nguyên không được sao chép hoặc có ngữ nghĩa sao chép chuyên biệt. Ngược lại cũng đúng: bất kỳ lớp nào không thể sao chép hoặc cần ngữ nghĩa chuyên biệt sao chép là quản lý tài nguyên. "Quản lý tài nguyên" trong lingua C++ trong thực tế có nghĩa là chịu trách nhiệm về một số không gian trong bộ nhớ, hoặc kết nối với mạng hoặc cơ sở dữ liệu, hoặc xử lý một tệp hoặc hoàn tác giao dịch, v.v.

Quản lý tài nguyên nắm bắt khá nhiều ví dụ. Đây là những trách nhiệm thực hiện một hoạt động tiền tố, một hoạt động hậu tố và có thể một số hành động ở giữa. Ví dụ, quản lý bộ nhớ có liên quan đến việc xử lý một địa chỉ bộ nhớ mà chúng tôi sẽ quản lý, có thể gây rối với bộ nhớ đó và cuối cùng giải phóng xử lý (vì nếu bạn yêu thích thứ gì đó, hãy để nó miễn phí).

template<typename T> 
struct memory { 
    memory(T const& val = T()) : p(new T(val)) { } 
    ~memory() { delete p } 
    T& operator*() const { return *p; } 
private: 
    T* p; 
}; 

// ... 
{ 
    memory<int> m0; 
    *m0 = 3; 
    std::cout << *m0 << '\n'; 
} 

này lớp memory là gần như chính xác: nó tự động mua lại các không gian bộ nhớ cơ bản và tự động giải phóng nó, ngay cả khi một ngoại lệ truyền một thời gian sau khi nó được mua lại tài nguyên của nó. Nhưng xem xét kịch bản này:

{ 
    memory<double> m1(3.14); 
    memory<double> m2(m1); // m2.p == m1.p (do you hear the bomb ticking?) 
} 

Bởi vì chúng tôi đã không cung cấp ngữ nghĩa bản chuyên ngành cho memory, trình biên dịch cung cấp constructor sao chép riêng của mình và sao chép chuyển nhượng. Những điều này làm sai điều: m2 = m1 có nghĩa là m2.p = m1.p, sao cho hai con trỏ trỏ đến cùng một địa chỉ.Đó là sai bởi vì khi m2 vượt quá phạm vi nó giải phóng tài nguyên của nó như một đối tượng chịu trách nhiệm tốt, và khi m1 sau đó đi ra khỏi phạm vi nó cũng giải phóng tài nguyên của nó, cùng một tài nguyên m2 đã được giải phóng, hoàn thành việc xóa hai lần - một khét tiếng kịch bản hành vi không xác định. Hơn nữa, trong C++ nó rất dễ tạo bản sao của một đối tượng mà không cần chú ý: một hàm lấy tham số của nó theo giá trị, trả về tham số của nó theo giá trị, hoặc tham số của nó theo tham chiếu nhưng sau đó gọi hàm khác mà chính nó lấy (hoặc trả về) thông số theo giá trị ... Sẽ dễ dàng hơn khi giả định rằng mọi thứ sẽ cố gắng sao chép.

Tất cả điều này để nói rằng khi một nhóm 'raison d'être đang quản lý một nguồn tài nguyên thì bạn ngay lập tức nên biết rằng bạn cần phải xử lý sao chép. Bạn nên quyết định

  • bạn hỗ trợ sao chép, trong khi bạn quyết định những gì sao chép có nghĩa là: chia sẻ an toàn của tài nguyên, thực hiện một bản sao sâu của tài nguyên cơ bản như vậy không có chia sẻ nào, hoặc kết hợp cả hai cách tiếp cận như trong copy-on-write hoặc bản sao lười biếng. Bất kỳ đường dẫn nào bạn chọn, bạn sẽ cần cung cấp một hàm tạo bản sao chuyên biệt toán tử gán bản sao.
  • hoặc bạn không hỗ trợ bất kỳ loại sao chép tài nguyên nào, trong trường hợp này bạn vô hiệu hóa hàm tạo bản sao và toán tử gán bản sao.

Tôi muốn đi xa và nói rằng quản lý tài nguyên là trường hợp duy nhất bạn vô hiệu hóa sao chép hoặc cung cấp ngữ nghĩa bản sao chuyên biệt. Đây chỉ là một quan điểm khác trên The Rule of Three.

0

khi bạn được phép tạo đối tượng chỉ sau khi kiểm tra như trong trường hợp của singleton u cần các nhà thầu riêng. khi hàm tạo được gọi là cá thể đối tượng sẽ được gọi và sau đó không có điểm nào trong việc kiểm tra nếu có một cá thể khác đã có. vì vậy những gì chúng ta làm là gọi một hàm thành viên của lớp từ chính và bên trong hàm thành viên đó kiểm tra xem một thể hiện khác đã có trong bộ nhớ chưa. nếu không phải hàm tạo được gọi. bị hủy bỏ. kiểm tra các lớp đơn hoặc các lớp được bảo vệ khác, nơi dữ liệu của đối tượng phải được giữ an toàn và không được phép sao chép.

cũng kiểm tra điều này: Singleton Class in C++

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