Tránh có một phương thức khởi tạo


12

Tôi có mã hiện tại nơi họ có một lớp và một phương thức khởi tạo trong lớp đó. Dự kiến ​​một khi đối tượng của lớp được tạo, họ cần gọi khởi tạo trên nó.

Lý do tại sao phương thức khởi tạo tồn tại Đối tượng được tạo sớm để có phạm vi toàn cục và sau đó phương thức khởi tạo sẽ được gọi sau khi tải một dll mà nó phụ thuộc vào.

Vấn đề với việc khởi tạo Lớp bây giờ có bool isInitialized này cần được kiểm tra trong mọi phương thức trước khi nó tiến hành và trả về lỗi nếu nó không được khởi tạo. Nói một cách đơn giản, đó là một nỗi đau lớn.

Một giải pháp có thể Khởi tạo trong hàm tạo. Chỉ có một con trỏ đến đối tượng trong phạm vi toàn cầu. Tạo đối tượng thực tế sau khi dll được tải.

Vấn đề với giải pháp trên Bất cứ ai tạo ra một đối tượng của lớp này cần biết rằng nó chỉ cần được tạo sau khi dll được tải nếu không nó sẽ thất bại.

điều này có chấp nhận được không?


Tôi đã gặp vấn đề tương tự khi tạo các đối tượng liên quan đến OpenGL cần được khởi tạo trước khi bối cảnh OpenGL tồn tại, nhưng được cho là giữ các đối tượng phụ thuộc OpenGL như danh sách, kết cấu, v.v.

3
Câu hỏi ngu ngốc # 1: Tại sao trình xây dựng đối tượng không thể tải DLL nếu nó chưa được tải?
John R. Strohm

1
Xin lỗi vì đã hồi sinh một câu hỏi cũ, nhưng trong trường hợp bất cứ ai đang đọc điều này trong năm 2017, hãy sử dụng call_oncetrong C ++ 11 . Các dự án chưa có trên C ++ 11 nên nghiên cứu cách thực hiện call_once trong C ++ 11 (tập trung vào vấn đề mà nó giải quyết, và sau đó như thế nào), sau đó triển khai lại theo hương vị (cũ) của C ++. Nó cần một nguyên thủy đồng bộ hóa đa luồng an toàn, có trạng thái cần được khởi tạo tĩnh (với giá trị không đổi). Lưu ý rằng trình biên dịch trước C ++ 11 có thể có các đặc điểm riêng cần được thỏa mãn.
rwong

Câu trả lời:


7

Âm thanh như một công việc cho một proxy ảo.

Bạn có thể tạo một proxy ảo chứa tham chiếu đến đối tượng được đề cập. Mặc dù DLL không được tải, Proxy có thể cung cấp một số hành vi mặc định nhất định cho khách hàng, khi DLL được tải, proxy sẽ chỉ chuyển tiếp tất cả các yêu cầu đến chủ đề thực.

Proxy ảo sẽ chịu trách nhiệm kiểm tra khởi tạo DLL và dựa trên quyết định này nếu yêu cầu được ủy quyền cho chủ đề thực.

Mẫu proxy trong Wikipedia

Làm thế nào để bạn thích ý tưởng này?


bạn không thực sự giải quyết nhiều ở đây. đối với mọi phương thức được thêm vào trong đối tượng thực tế, bạn cũng cần thêm phương thức đó vào proxy. Không?

Điều này tránh được vấn đề ghi nhớ để gọi init. chức năng, nhưng có vẻ như mọi chức năng trong proxy vẫn cần kiểm tra xem liệu đã được tải hay chưa.

1
@Sriram bây giờ có sự khác biệt lớn, sử dụng proxy bạn đã hạn chế bảo trì liên quan đến kiểm tra DLL đối với một lớp duy nhất: proxy. Các khách hàng của lớp thậm chí sẽ không cần biết về việc kiểm tra DLL. Vì hầu hết các phương thức sẽ thực hiện ủy quyền, tôi không thấy có vấn đề gì khi phải triển khai giao diện trong cả chủ đề thực trong proxy. Nói cách khác, bạn có thể ngăn việc tạo chủ đề thực cho đến khi bạn chắc chắn rằng DLL đã được tải, và sau đó bạn sẽ không cần phải kiểm tra trạng thái DLL mỗi lần.

@edalorzo: trong khi ý tưởng là tốt, vấn đề ở đây là chúng ta đã nói về một proxy và OP đang phàn nàn về việc phải kiểm tra từng phương thức proxy của nó ...
Matthieu M.

4

Không có gì hữu ích xảy ra nếu DLL chưa được tải; đối tượng chỉ tạo ra một lỗi. Đó có phải là một lỗi nghiêm trọng? Lỗi được xử lý như thế nào?

Phương pháp thử nghiệm của bạn có đảm bảo rằng lỗi này không bao giờ xảy ra trong sản phẩm hoàn chỉnh không?

Nghe có vẻ như một công việc để thử nghiệm, không phải cho thiết kế kiến ​​trúc. Đừng trả lại lỗi, assertđiều đó không bao giờ xảy ra.

Khắc phục bất kỳ ứng dụng khách nào của lớp hiện bắt và bỏ qua lỗi để không thử và gây ra lỗi đó ngay từ đầu.

Một mẫu đa luồng có thể là để đặt biến điều kiện dùng chung sau khi tải DLL và chờ đợi hàm tạo của đối tượng (và chặn các luồng khác) cho đến khi DLL được tải.


Tôi nghĩ bạn đã đúng, không có gì sai khi ném một ngoại lệ khi tạo đối tượng được đề cập nếu DLL không được khởi tạo đúng cách. Mã khách hàng sau đó sẽ chỉ cần xử lý ngoại lệ này và thử nghiệm phải đảm bảo rằng ngoại lệ đó được xử lý thích hợp.

2

Điều đầu tiên: tránh các đối tượng toàn cầu là dịch hại, trừ khi trạng thái của chúng không bao giờ thay đổi (cấu hình).

Bây giờ, nếu bạn bị mắc kẹt với nó, vì bất kỳ lý do gì, có một số thiết kế có thể có thể giúp bạn.

Tôi đã đưa ra hai ý tưởng:

  1. Sử dụng một Mặt tiền để tải DLL. Đối tượng chỉ có thể được truy cập thông qua Mặt tiền, Mặt tiền tải DLL khi tạo và khởi tạo Đối tượng cùng một lúc.

  2. Sử dụng Proxy, nhưng loại thông minh;)

Hãy để tôi giải thích về điểm thứ hai, vì tôi sợ rằng câu trả lời của @edalorzo có thể khiến bạn sợ hãi:

// Private
static Object& GetObjectImpl() { static Object O; return O; }

// Public
static Object& GetObject() {
  Object& o = GetObjectImpl();
  assert(o.isInitialized() && "Object not initialized yet!");
  return o;
}

Bây giờ bạn chỉ có một kiểm tra duy nhất.

Điều này cũng có thể được thực hiện thông qua một số loại con trỏ thông minh:

Pointer<Object> o;

Ở đây bạn chỉ dự trữ không gian cho một con trỏ, ban đầu là null và chỉ khi DLL được tải, bạn mới thực sự phân bổ đối tượng. Tất cả các truy cập trước đó sẽ đưa ra một ngoại lệ (NullException: p?) Hoặc chấm dứt chương trình (một cách sạch sẽ).


1

Đây là một câu hỏi mẹo. Tôi cảm thấy rằng kiến ​​trúc có thể giúp đỡ với vấn đề.

Từ các bằng chứng, có vẻ như không được tải khi chương trình được tải. Nó gần giống như một plugin.

Một điều mà tôi suy nghĩ là bạn phải khởi tạo dll. Lập trình viên phải giả định rằng đối tượng sẽ không quay trở lại một cách đáng tin cậy, dù sao đi nữa. Bạn có thể có thể tận dụng lợi thế này ( bool isObjectLoaded() { return isInitialized; }), nhưng chắc chắn bạn sẽ nhận được nhiều báo cáo lỗi hơn qua séc của mình.

Tôi đã nghĩ về một singleton.

Nếu bạn gọi singleton, nó sẽ trả về đúng đối tượng nếu nó có thể truy xuất một đối tượng. Nếu nó không thể trả về đúng đối tượng, thì bạn sẽ nhận được một số giá trị lỗi / nullptr / đối tượng cơ sở trống. Điều này sẽ không hoạt động nếu đối tượng có thể chết trên bạn, mặc dù.

Ngoài ra, nếu bạn cần nhiều phiên bản của đối tượng, bạn có thể sử dụng một cái gì đó như Factory thay thế.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.