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 và 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.
Một singleton sẽ là một ví dụ. –