1. Biên dịch sạch sẽ ở mức độ cảnh báo cao

Estimated reading: 8 minutes 369 views

Tóm tắt

Hãy đặt những cảnh báo (warning) vào trong tim bạn: Sử dụng mức cảnh báo cao nhất của trình biên dịch. Yêu cầu bản mã sạch (không có cảnh báo). Hiểu tất cả các cảnh báo. Loại bỏ cảnh báo bằng cách thay đổi mã của bạn chứ không phải bằng cách hạ mức cảnh báo.

Thảo luận

Trình biên dịch là bạn. Nếu nó đưa ra cảnh báo cho một cấu trúc nhất định thì thường tiềm ẩn vấn đề trong mã code của bạn. Các bản build thành công phải ở chế độ im lặng (không có cảnh báo). Nếu không làm được như vậy, bạn sẽ nhanh chóng có thói quen xem lướt qua đầu ra và sẽ bỏ lỡ những vấn đề thực sự tiềm tàng (Mục 2).

Để loại bỏ cảnh báo: a) hiểu cảnh báo đó; và sau đó b) viết lại mã của bạn để loại bỏ cảnh báo và làm cho mã code của bạn sạch sẽ hơn đối với cả con người + trình biên dịch.

  • Làm điều này ngay cả khi chương trình có vẻ chạy chính xác ngay từ đầu.
  • Làm điều này ngay cả khi bạn khẳng định rằng cảnh báo đó là lành tính. Ngay cả những cảnh báo lành tính cũng có thể che khuất những cảnh báo sau này chỉ ra những mối nguy hiểm thực sự.

Ví dụ

Ví dụ 1: File tiêu đề của bên thứ 3.

Một số file tiêu đề thư viện mà bạn không thể thay đổi có thể chứa cấu trúc gây ra cảnh báo (có thể là lành tính). Tạo phiên bản file tiêu đề của riêng bạn bằng cách dùng #include bọc (wrapper) file tiêu đề bên thứ 3. Trong file của bạn, tắt có chọn lọc các cảnh báo gây, sau đó dùng file wrapper của bạn xuyên suốt dự án bằng cách dùng #include. Chú ý rằng cú pháp điều khiển cảnh báo sẽ khác nhau tùy theo trình biên dịch.
PS: Bạn có biết tại sao cần phải làm như trên không?
				
					// File: myproj/myjambda.h -- wraps Boost's lambda.hpp
// Always include this file; don't use lambda.hpp directly.
// NOTE: Our build now automatically checks "grep lambda.hpp <srcfile>".
// Boost.Lambda produces noisy compiler warnings that we know are innocuous.
// When they fix it we'll remove the pragmas below, but this header will still exist
//
#pragma warning(push) //disable for this header only
#pragma warning(disable:4512)
#pragma warning(disable:4180)
#include <boost/lambda/lambda.hpp>
#pragma warning(pop) //restore original warning level				
			

Ví dụ 2: Tham số hàm không sử dụng

Kiểm tra kỹ xem bạn không có ý định sử dụng tham số hàm nào (ví dụ: tham số này có thể giữ chỗ cho mở rộng trong tương lai, hoặc mã code của bạn không sử dụng nhưng đó có thể là một phần bắt buộc của chữ ký chuẩn hóa). Nếu không cần thiết, chỉ cần xóa tên của tham số hàm.
				
					//... inside a user-defined allocator that has no use for the hint... 
// warning: "unused parameter 'localityHint'"
pointer allocate(sizejype numObjects, const void *localityHint = 0){
    return static_cast<pointer>( mallocShared( numObjects * sizeof(T)));
}
// new version: eliminates warning
pointer allocate(sizejype numObjects, const void * /* localityHint */ = 0 ){
    return static_cast<pointer>( mallocShared( numObjects * sizeof(T)));
}				
			

Ví dụ 3: Biến được khai báo nhưng không được sử dụng

 Chúng ta phải xác nhận rõ những biến số nào không được sử dụng. Ví dụ những đối tượng (object) sử dụng ngăn xếp RAII (RAII stack-based) thường tạo ra những cảnh báo giả như vậy. Nếu không cần thiết chúng ta có thể tắt cảnh báo này của trình biên dịch, ví dụ như bằng dòng lệnh gọi biến số đó (và điều này hoàn toàn không ảnh hưởng đến tốc độ chương trình khi chạy thực tế).
				
					// warning: "variable lock' is defined but never used"
void Fun() {
    Lock lock;
}
// new version: probably eliminates warning void
Fun() {
    Lock lock;
    lock;
}				
			

Ví dụ 4: Biến số được sử dụng mà không khởi tạo

Hãy khởi tạo biến, tham khảo mục 19.

Ví dụ 5: Không có lệnh dòng lệnh return

Có nhiều trường hợp trình biên dịch sẽ yêu cầu có dòng lệnh return cho đến tận cuối cùng của hàm, kể cả trên thực tế không bao giờ điều kiện rẽ nhánh đến được đó (Ví dụ: Vòng lặp vô hạn, lệnh throw, hay các lệnh return khác). Tuy nhiên ngay cả khi chúng ta nghĩ là không bao giờ rẽ nhánh đến được đó thì cũng có khả năng là bị nhầm, do đó cảnh báo trên sẽ giúp được chúng ta.
Ví dụ lệnh rẽ nhánh switch mà không có default, thường không được linh hoạt khi sửa đổi (điều kiện tăng lên nhưng quên không thêm case…). Cho nên luôn chắc chắn rằng cho thêm default và có thể assert(false) chẳng hạn, tham khảo thêm mục 68, 90.
				
					//warning: missing "return"
int Fun(Color c){
    switch(c){
    case Red:   return 2;
    case Green: return 0;
    case Blue:
    case Black: return 1;
    }
}
// new version: eliminates warning
int Fun(Color c){
    switch(c) {
    case Red:   return 2;
    case Green: return 0;
    case Blue:
    case Black: return 1;
    default:    assert("should never get here!" );
                return -1;
    }
}				
			

Ví dụ 6: Không thống nhất kiểu signed / unsigned

Thông thường khi lập trình chúng ta không cần so sánh hay gán các kiểu dữ liệu số nguyên có dấu và không dấu qua lại lẫn nhau. Hãy đổi kiểu dữ liệu nếu như cần so sánh hoặc gán dữ liệu. Trong trường hợp xấu nhất hãy ép kiểu (Thay vì để trình biên dịch tạo cảnh báo chúng ta tường minh luôn việc đó).


Ngoại lệ

Đôi khi, trình biên dịch có thể đưa ra một cảnh báo vô vị hoặc thậm chí giả mạo (tức là cảnh báo đó chỉ là nhiễu) nhưng không có cách nào để tắt cảnh báo đó và việc viết lại mã code để tắt cảnh báo có thể là không khả thi hoặc không hiệu quả. Trong những trường hợp hiếm hoi này, với tư cách là Leader của nhóm, hãy tránh làm việc vô vị xung quanh một cảnh báo tẻ nhạt: Tắt cảnh báo, vô hiệu hóa nó cục bộ nhất có thể và viết nhận xét rõ ràng ghi lại lý do tại sao.

Tham khảo

[Meyers97] §48 • [Stroustrup94] §2.6.2

Leave a Comment

Chia sẻ:

1. Biên dịch sạch sẽ ở mức độ cảnh báo cao

Or copy link

CONTENTS