Bỏ qua những kẻ hủy diệt của người Viking trong C đang đưa YAGNI đi quá xa?


9

Tôi đang làm việc trên một ứng dụng nhúng trung bình trong C bằng các kỹ thuật tương tự OO. Các "lớp" của tôi là các mô đun .h / .c sử dụng các cấu trúc dữ liệu và các cấu trúc con trỏ hàm để mô phỏng đóng gói, đa hình và tiêm phụ thuộc.

Bây giờ, người ta sẽ mong đợi một myModule_create(void)chức năng đi kèm với một myModule_destroy(pointer)đối tác. Nhưng dự án đang được nhúng, các tài nguyên được khởi tạo thực tế không bao giờ nên được phát hành.

Ý tôi là, nếu tôi có 4 cổng nối tiếp UART và tôi tạo 4 phiên bản UART với các chân và cài đặt cần thiết của chúng, thì hoàn toàn không có lý do gì để muốn phá hủy UART # 2 tại một số điểm trong thời gian chạy.

Vì vậy, theo nguyên tắc YAGNI (Bạn sẽ không cần nó), tôi có nên bỏ qua các hàm hủy không? Điều này có vẻ cực kỳ lạ đối với tôi nhưng tôi không thể nghĩ ra cách sử dụng chúng; tài nguyên được giải phóng khi thiết bị tắt nguồn.


4
Nếu bạn không cung cấp một cách để loại bỏ đối tượng, bạn đang truyền một thông điệp rõ ràng rằng chúng có "vô hạn" trọn đời một khi được tạo. Nếu điều này có ý nghĩa với ứng dụng của bạn, tôi nói: làm điều đó.
glampert

4
Nếu bạn sẽ đi xa đến mức kết hợp loại với trường hợp sử dụng cụ thể của mình, tại sao thậm chí còn có myModule_create(void)chức năng? Bạn chỉ có thể mã hóa các trường hợp cụ thể mà bạn muốn sử dụng vào giao diện bạn trưng ra.
Doval

@Doval Tôi nghĩ về nó. Tôi là nhân viên thực tập sử dụng các bộ phận và bit mã từ người giám sát của mình, vì vậy tôi đang cố gắng thực hiện "làm đúng", thử nghiệm phong cách OO trong C để có kinh nghiệm và nhất quán với các tiêu chuẩn của công ty.
Asics

2
@glampert đinh nó; Tôi muốn nói thêm rằng bạn nên làm sạch tuổi thọ vô hạn dự kiến ​​trong tài liệu của hàm tạo.
Blrfl

Câu trả lời:


11

Nếu bạn không cung cấp một cách để loại bỏ đối tượng, bạn đang truyền một thông điệp rõ ràng rằng chúng có "vô hạn" trọn đời một khi được tạo. Nếu điều này có ý nghĩa với ứng dụng của bạn, tôi nói: làm điều đó.

Glampert là đúng; không cần thiết cho tàu khu trục ở đây. Họ sẽ chỉ tạo ra sự phình to mã và một cạm bẫy cho người dùng (sử dụng một đối tượng sau khi hàm hủy của nó được gọi là hành vi không xác định).

Tuy nhiên, bạn nên chắc chắn rằng thực sự không cần phải vứt bỏ đồ vật. Chẳng hạn, bạn có cần phải có một đối tượng cho một UART hiện không được sử dụng không?


3

Cách dễ nhất tôi đã tìm thấy để phát hiện rò rỉ bộ nhớ là có thể thoát khỏi ứng dụng của bạn một cách sạch sẽ. Nhiều trình biên dịch / môi trường cung cấp một cách để kiểm tra bộ nhớ vẫn được phân bổ khi ứng dụng của bạn thoát. Nếu không được cung cấp, thường có một cách để thêm một số mã ngay trước khi thoát mà có thể tìm ra nó.

Vì vậy, tôi chắc chắn sẽ cung cấp các hàm tạo, hàm hủy và logic tắt ngay cả trong một hệ thống nhúng mà "về mặt lý thuyết" không bao giờ thoát ra để dễ dàng phát hiện rò rỉ bộ nhớ. Trong thực tế, phát hiện rò rỉ bộ nhớ thậm chí còn quan trọng hơn nếu mã ứng dụng không bao giờ thoát ra.


Lưu ý rằng điều này không áp dụng nếu lần duy nhất bạn thực hiện phân bổ là trong khi khởi động, đây là mô hình tôi nghiêm túc xem xét trong các thiết bị hạn chế bộ nhớ.
CodeInChaos

@ Mã: Không có lý do để giới hạn bản thân. Mặc dù bạn có thể đưa ra thiết kế tuyệt vời phân bổ bộ nhớ khi khởi động, nhưng khi những người sau khi bạn đi cùng không biết gì về kế hoạch lớn này hoặc không thấy tầm quan trọng của nó, họ sẽ phân bổ bộ nhớ cho bay và có thiết kế của bạn. Chỉ cần làm đúng và phân bổ / hủy phân bổ và xác minh rằng những gì bạn thực hiện thực sự hoạt động. Nếu bạn thực sự có một thiết bị bị hạn chế bộ nhớ thì điều thường được thực hiện là ghi đè lên các khối phân bổ toán tử / malloc và prereserve mới.
Dunk

3

Trong quá trình phát triển của tôi, việc sử dụng rộng rãi các loại dữ liệu mờ để thúc đẩy cách tiếp cận giống như OO, tôi đã quá vật lộn với câu hỏi này. Lúc đầu, tôi đã quyết định loại bỏ kẻ hủy diệt khỏi viễn cảnh YAGNI, cũng như viễn cảnh "mã chết" của MISRA. (Tôi có rất nhiều phòng tài nguyên, đó không phải là một sự cân nhắc.)

Tuy nhiên ... việc thiếu một bộ hủy có thể làm cho việc kiểm tra khó khăn hơn, như trong kiểm tra đơn vị / tích hợp tự động. Thông thường, mỗi thử nghiệm phải hỗ trợ thiết lập / phá vỡ để các đối tượng có thể được tạo, thao tác, sau đó hủy. Chúng bị phá hủy để đảm bảo một điểm khởi đầu sạch sẽ, chưa được xử lý cho thử nghiệm sau đó. Để làm điều này, lớp cần một hàm hủy.

Do đó, theo kinh nghiệm của tôi, "không" trong YAGNI hóa ra là "được" và cuối cùng tôi đã tạo ra các hàm hủy cho mọi lớp cho dù tôi nghĩ tôi có cần hay không. Ngay cả khi tôi bỏ qua kiểm tra, ít nhất một kẻ hủy diệt được thiết kế chính xác tồn tại cho khẩu hiệu kém theo tôi sẽ có một. (Và, với giá trị thấp hơn nhiều, nó làm cho mã có thể tái sử dụng nhiều hơn ở chỗ nó có thể được sử dụng trong môi trường mà nó sẽ bị phá hủy.)

Trong khi địa chỉ YAGNI, nó không giải quyết mã chết. Vì thế, tôi thấy rằng một macro biên dịch có điều kiện giống như #define BUILD_FOR_TESTING cho phép loại bỏ hàm hủy khỏi bản dựng sản xuất cuối cùng.

Thực hiện theo cách này: bạn có hàm hủy để thử nghiệm / tái sử dụng trong tương lai và bạn đáp ứng các mục tiêu thiết kế của YAGNI và quy tắc "không có mã chết".


Hãy cẩn thận về # ifdef'ing mã kiểm tra / prod của bạn. Nó hợp lý an toàn khi được áp dụng cho toàn bộ một chức năng, như bạn mô tả, bởi vì nếu thực tế chức năng đó là cần thiết, quá trình biên dịch sẽ thất bại. Tuy nhiên, sử dụng #ifdef nội tuyến trong một chức năng sẽ rủi ro hơn nhiều vì bạn hiện đang thử nghiệm một loại tiền mã hóa khác với những gì đang chạy trong prod.
Kevin

0

Bạn có thể có một hàm hủy không op, giống như

  void noop_destructor(void*) {};

sau đó thiết lập hàm hủy của Uartcó lẽ bằng cách sử dụng

  #define Uart_destructor noop_destructor

(thêm dàn diễn viên phù hợp nếu cần thiết)

Đừng quên tài liệu. Có thể bạn muốn

 #define Uart_destructor abort

Ngoài ra, trường hợp đặc biệt trong mã phổ biến gọi hàm hủy là trường hợp khi hàm con trỏ hàm hủy là NULLđể tránh gọi nó.

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.