2008-10-20 16 views
13

Trong Visual Studio, tôi thường chỉ sử dụng các đối tượng cho mục đích RAII. Ví dụ:Xử lý với C++ "đã khởi tạo nhưng không được tham chiếu" cảnh báo về việc hủy bỏ người trợ giúp phạm vi?

ScopeGuard close_guard = MakeGuard(&close_file, file); 

Toàn bộ mục đích của close_guard là để đảm bảo rằng các tập tin sẽ được gần gũi về lối ra chức năng, nó không được sử dụng bất cứ nơi nào khác. Tuy nhiên, Visual Studio cho tôi cảnh báo rằng "biến số địa phương được khởi tạo nhưng không được tham chiếu". Tôi muốn tắt cảnh báo này cho trường hợp cụ thể này.

Làm thế nào để bạn đối phó với loại tình huống này? Visual Studio nghĩ rằng đối tượng này là vô ích, nhưng điều này là sai vì nó có một destructor không tầm thường.

Tôi không muốn sử dụng chỉ thị #pragma cảnh báo vì điều này sẽ tắt cảnh báo này ngay cả vì lý do chính đáng.

+0

Bạn có thể đăng mã cho ScopeGuard và MakeGuard (hoặc một số phiên bản cắt giảm vẫn hiển thị hành vi) không? –

+0

Vui lòng đọc http://www.ddj.com/cpp/184403758 để có thể triển khai. –

+0

Bạn đang xem phiên bản MSVC nào trong hành vi này? – MSN

Trả lời

5

Phương pháp 1: Sử dụng chỉ thị #pragma warning.

#pragma warning cho phép sửa đổi chọn lọc hành vi của thông điệp cảnh báo trình biên dịch.

#pragma warning(push) 
#pragma warning(disable : 4705) // replace 4705 with warning number 

ScopeGuard close_guard = MakeGuard(&close_file, file); 

#pragma warning(pop) 

Mã này sẽ tắt cảnh báo mã cảnh báo cụ thể và sau đó khôi phục trạng thái cảnh báo đã lưu cuối cùng.

Cách 2: Sử dụng giải pháp thay thế như sau. Visual Studio sẽ hạnh phúc và bạn cũng thế. Cách giải quyết này được sử dụng trong nhiều mẫu của Microsoft và cũng trong các dự án khác.

ScopeGuard close_guard = MakeGuard(&close_file, file); 
close_guard; 

Hoặc bạn có thể tạo #define để giải quyết cảnh báo.

#define UNUSED_VAR(VAR) VAR 
... 
ScopeGuard close_guard = MakeGuard(&close_file, file); 
UNUSED_VAR(close_guard); 

Một số người dùng nói rằng mã được trình bày sẽ không làm việc vì ScopeGuard là một typedef. Giả định này là sai.

http://www.ddj.com/cpp/184403758

Theo ++ Chuẩn C, một tham chiếu khởi tạo với một giá trị tạm thời làm cho rằng giá trị tạm thời sống cho tuổi thọ của tài liệu tham khảo riêng của mình.

+0

Điều này đã được đăng trong [Tạp chí Dobb] (http://www.ddj.com/cpp/184403758) của Andrei Alexandrescu và Petru Marginean. Đây là hai trình chơi C++ có nền tảng mạnh. Và tôi cũng chưa bao giờ có hành vi như vậy. Sẽ kiểm tra nó và bình luận sau đó. –

+0

ScopeGuard thực chất là một typedef cho một kiểu tham chiếu, nhưng điều này không sai. Trích từ liên kết của smink: "Theo tiêu chuẩn C++, tham chiếu được khởi tạo với giá trị tạm thời làm cho giá trị tạm thời đó tồn tại trong suốt thời gian tồn tại của tham chiếu". –

+0

Vâng đó là phần quan trọng trong bài viết làm cho cơ chế hoạt động. Thực tế là ScopeGuard là một typedef không làm cho mã sai và nó vẫn hoạt động. Ngay cả khi đó là như vậy, chúng tôi sẽ tranh luận một chủ đề hoàn toàn khác và không phải là câu hỏi. –

0

Thử thêm 'dễ bay hơi' vào khai báo ScopeGuard.

+0

Điều đó sẽ làm gì? –

+0

Không có gì thực sự trong trường hợp này, ngoại trừ có thể im lặng cảnh báo trình biên dịch. – Aaron

+0

Dễ bay hơi nói với trình biên dịch rằng biến có thể được thay đổi bên ngoài kiến ​​thức của nó. Tôi sử dụng nó rất nhiều cho các thanh ghi phần cứng. Nếu biến là dễ bay hơi, trình biên dịch phải tạo ra nó và gán nó. – Robert

3

Chúng tôi sử dụng:

static_cast<void>(close_guard); 

cho các biến mà trình biên dịch được khiếu nại.

+0

Tôi muốn nói rằng việc bỏ trống có thể là một tình huống mà một dàn diễn viên kiểu C được hợp lý trong C++ trên một static_cast ... –

1

Bạn có thể phạm vi cảnh báo #pragma xung quanh rằng dòng mã chỉ bằng cách sử dụng

#pragma warning(push) 
#pragma warning(disable:XXXX) 
your code here; 
#pragma warning(pop) 

hoặc

#pragma warning(disable:XXXX) 
your code here; 
#pragma warning(default:XXXX) 

Bạn cũng có thể sử dụng UNREFERENCED_PARAMETER(close_guard); sau dòng mã bên trên.

3

Trong một số VC++ file header, MS định nghĩa một macro:

#define UNUSED(x) x 

sử dụng như:

ScopeGuard close_guard = MakeGuard(&close_file, file); 
UNUSED(close_guard); 

nào im lặng cảnh báo và các văn bản đó.

+0

UNUSED_ALWAYS cũng được định nghĩa trong hầu hết các chương trình cửa sổ và hoạt động theo cùng một cách. –

8

Nếu đối tượng của bạn có một destructor không tầm thường, Visual Studio nên không được cung cấp cho bạn cảnh báo đó. Đoạn mã sau không tạo ra bất kỳ cảnh báo nào trong VS2005 với cảnh báo chuyển tất cả các con đường lên (/ W4):


class Test 
{ 
public: 
    ~Test(void) { printf("destructor\n"); } 
}; 

Test foo(void) { return Test(); } 

int main(void) 
{ 
    Test t = foo(); 
    printf("moo\n"); 

    return 0; 
} 

Nhận xét rằng trình phá hủy đưa ra cảnh báo; mã as-is không.

+0

Vấn đề có vẻ là 'ScopeGuard' thực sự là một typedef tham chiếu đến một đối tượng với một destructor không tầm thường. VS dường như không đủ thông minh để giải quyết vấn đề này một cách chính xác. –

0

tôi sử dụng bài Smink của trên và chỉ cần thêm rằng tôi dính một lời nhận xét bên cạnh #define nói // sử dụng để ngăn chặn cảnh báo [số cảnh báo] trong visual studio

-3

Vấn đề cốt lõi ở đây dường như thực sự được rằng trình biên dịch không hoàn toàn hiểu những gì bạn đang đi ... mà có vẻ là sử dụng ngữ nghĩa ngữ nghĩa trong C++ để nhận được một số mã được gọi khi một biến được deallocated ngay cả khi nó không được sử dụng. Đúng? Đó là cơ chế chính nó tấn công tôi như borderline ... một trình biên dịch nên có quyền loại bỏ các biến không sử dụng nhưng ngữ nghĩa xây dựng C++ thực sự messes những điều này lên. Không có cách nào khác để làm điều này mà là ít sleight-of-tay?

+1

RAII là một kỹ thuật mạnh mẽ. Bạn thực sự nên cân nhắc việc đọc http://www.ddj.com/cpp/184403758 –

+0

Cảm ơn, điều đó khá thú vị. Nhưng trong đó, họ dường như truy cập đối tượng được tạo ra trong lời gọi ".dismiss()" dường như làm cho nó được sử dụng? Vì vậy, trình biên dịch VC sẽ phàn nàn sau đó? – jakobengblom2

+0

Nó sẽ không phàn nàn nếu bạn sử dụng ".dismiss()" chức năng, tất nhiên. Nhưng đây không phải là trường hợp ở đây;) –

2

Vâng, trong trường hợp này, ScopeGuard thực sự là typedef đối với loại tham chiếu. Điều này sẽ không làm việc không may.

Điều đó không có nghĩa là toàn bộ ScopeGuard không hoạt động, bởi vì trong trường hợp đó destructor sẽ không được gọi là ???

+0

Trong trường hợp ai đó đang tự hỏi nguồn gốc từ đâu. Đó là một nhận xét của OP về câu trả lời của 'Sherm Pendley' đã bị xóa. Tôi cho rằng Sherm đã xóa nó vì nhận xét cho rằng giải pháp của anh ta không thể sử dụng được. –

+0

Không, tôi có nghĩa là tuyên bố biến trước khi sử dụng nó sẽ không hoạt động vì nó thực sự là một tham chiếu. –

+0

Thực ra, điều đó tốt ... ràng buộc một tham chiếu đến tạm thời kéo dài tuổi thọ của tạm thời thành tham chiếu đó. Ví dụ: {string & s = string ("hello") + "world!"; cout << s; } mã này là tốt, bởi vì tạm thời được trả về bởi toán tử + sống cho đến khi dấu ngoặc đóng tiếp theo. – Aaron

1

Tôi đoán trong thực tế, tôi sẽ rất cừ khôi với #pragma tắt ... hoặc 'UNUSED'. Tuy nhiên, như một quy tắc chính, mã nên được giữ sạch cảnh báo ngay cả với chi phí của một số lượng lớn thêm. Nó sẽ biên dịch trong nhiều trình biên dịch khác nhau trên các nền tảng và hệ điều hành khác nhau mà không có cảnh báo. Nếu nó không, mã đã được cố định để nó. Duy trì mã tạo cảnh báo ở gcc -Wall level không phải là một ý tưởng hay.

Cảnh báo biên dịch là bạn của bạn và cần được chú ý là vấn đề hoặc nguyên tắc. Ngay cả khi nó có nghĩa là mọi thứ phải được thực hiện theo một cách to hơn và nhiều chi tiết hơn. Trả tiền cho chính nó trong thời gian dài khi mã được chuyển, duy trì và tồn tại mãi mãi ...

+0

Cảnh báo trình biên dịch này là pedantic. vô hiệu hóa nó hoàn toàn. Nó làm tổn hại đến mã tự viết trong các trường hợp, ví dụ: void foo (int/* numberOfWidgets * /); – Aaron

0

Bạn có thể tạo đối tượng ScopeGuardImpl1 một cách rõ ràng, miễn là không có quá nhiều tham số trong trường hợp bạn đang sử dụng kết quả là không thể đọc được. Bằng cách đó bạn sẽ tránh tham chiếu-khởi tạo-với-tạm thời mà cảnh báo VS dường như không hiểu. Chi phí là phải đánh vần mọi thứ từ lâu, thay vì lấy ma thuật MakeGuard.

3

Tôi muốn sử dụng macro tất cả các cách trong trường hợp này:

#define SCOPE_GUARD(guard, fn, param) \ 
    ScopeGuard guard = MakeGuard(fn, param); \ 
    static_cast<void>(guard) 

tại mã của bạn là tốt đẹp và ngắn:

SCOPE_GUARD(g1, &file_close, file1); 
SCOPE_GUARD(g2, &file_close, file2); 

Một ưu điểm của phương pháp này là sau này bạn có thể thêm __LINE__, __func__ v.v. để ghi lại các hành động bảo vệ sau này nếu cần.

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