Tôi đoán bạn đang thiếu một cái gì đó, ở đây.
chức năng tĩnh?
Khai báo một hàm static sẽ làm cho nó "ẩn" trong đơn vị biên dịch của nó.
Tên có phạm vi không gian tên (3.3.6) có liên kết nội bộ nếu nó là tên của
- một biến, hàm hoặc mẫu hàm được khai báo rõ ràng là tĩnh;
3.5 / 3 - C ++ 14 (n3797)
Khi một tên có liên kết nội bộ, thực thể mà nó biểu thị có thể được gọi bằng các tên từ các phạm vi khác trong cùng một đơn vị dịch.
3.5 / 2 - C ++ 14 (n3797)
Nếu bạn khai báo hàm tĩnh này trong tiêu đề, thì tất cả các đơn vị biên dịch bao gồm tiêu đề này sẽ có bản sao riêng của hàm.
Vấn đề là, nếu có các biến tĩnh bên trong hàm đó, thì mỗi đơn vị biên dịch bao gồm tiêu đề này cũng sẽ có phiên bản cá nhân của riêng chúng.
hàm nội tuyến?
Khai báo nội dòng làm cho nó trở thành một ứng cử viên cho nội tuyến (ngày nay nó không có nhiều ý nghĩa trong C ++, vì trình biên dịch sẽ nội tuyến hoặc không, đôi khi bỏ qua thực tế là từ khóa nội dòng có hay không):
Một khai báo hàm (8.3.5, 9.3, 11.3) với một trình xác định nội tuyến khai báo một hàm nội tuyến. Bộ chỉ định nội tuyến chỉ ra cho việc triển khai rằng việc thay thế nội tuyến của thân hàm tại điểm gọi được ưu tiên hơn cơ chế gọi hàm thông thường. Không cần triển khai để thực hiện thay thế nội tuyến này tại điểm gọi; tuy nhiên, ngay cả khi việc thay thế nội tuyến này bị bỏ qua, các quy tắc khác cho các hàm nội tuyến được xác định bởi 7.1.2 sẽ vẫn được tôn trọng.
7.1.2 / 2 - C ++ 14 (n3797)
Trong tiêu đề, nó có một tác dụng phụ thú vị: Hàm nội tuyến có thể được xác định nhiều lần trong cùng một mô-đun và trình liên kết sẽ chỉ cần nối "chúng" thành một (nếu chúng không được nội tuyến vì lý do của trình biên dịch).
Đối với các biến tĩnh được khai báo bên trong, tiêu chuẩn cho biết cụ thể có một và chỉ một trong số chúng:
Một biến cục bộ tĩnh trong một hàm nội tuyến bên ngoài luôn tham chiếu đến cùng một đối tượng.
7.1.2 / 4 - C ++ 98 / C ++ 14 (n3797)
(các hàm theo mặc định là extern, vì vậy, trừ khi bạn đánh dấu cụ thể hàm của mình là tĩnh, điều này sẽ áp dụng cho hàm đó)
Điều này có lợi thế là "tĩnh" (nghĩa là nó có thể được xác định trong tiêu đề) mà không có sai sót (nó tồn tại nhiều nhất một lần nếu nó không được nội dòng)
biến cục bộ tĩnh?
Các biến cục bộ tĩnh không có liên kết (chúng không thể được gọi bằng tên bên ngoài phạm vi của chúng), nhưng có thời lượng lưu trữ tĩnh (tức là nó là toàn cục, nhưng việc xây dựng và phá hủy nó tuân theo các quy tắc cụ thể).
tĩnh + nội tuyến?
Việc trộn nội tuyến và tĩnh sau đó sẽ dẫn đến hậu quả như bạn đã mô tả (ngay cả khi hàm được nội dòng, biến tĩnh bên trong sẽ không có và bạn sẽ kết thúc với nhiều biến tĩnh như bạn có các đơn vị biên dịch bao gồm định nghĩa về các hàm tĩnh của bạn ).
Câu trả lời cho câu hỏi bổ sung của tác giả
Kể từ khi tôi viết câu hỏi, tôi đã thử nó với Visual Studio 2008. Tôi đã cố gắng bật tất cả các tùy chọn làm cho VS hoạt động tuân thủ các tiêu chuẩn, nhưng có thể tôi đã bỏ sót một số tùy chọn. Đây là những kết quả:
Khi hàm chỉ đơn thuần là "nội tuyến", chỉ có một bản sao của biến tĩnh.
Khi hàm là "nội tuyến tĩnh", có bao nhiêu bản sao có đơn vị dịch.
Câu hỏi thực sự bây giờ là liệu mọi thứ được cho là theo cách này, hay đây là một đặc điểm riêng của trình biên dịch Microsoft C ++.
Vì vậy, tôi cho rằng bạn có một cái gì đó như thế:
void doSomething()
{
static int value ;
}
Bạn phải nhận ra rằng biến static bên trong hàm, nói một cách đơn giản, là một biến toàn cục ẩn đối với tất cả trừ phạm vi của hàm, nghĩa là chỉ hàm được khai báo bên trong mới có thể tiếp cận được.
Nội tuyến hàm sẽ không thay đổi bất cứ điều gì:
inline void doSomething()
{
static int value ;
}
Sẽ chỉ có một biến toàn cục ẩn. Thực tế là trình biên dịch sẽ cố gắng nội dòng mã sẽ không thay đổi thực tế là chỉ có một biến ẩn toàn cầu.
Bây giờ, nếu hàm của bạn được khai báo là static:
static void doSomething()
{
static int value ;
}
Sau đó, nó là "riêng tư" cho mỗi đơn vị biên dịch, có nghĩa là mọi tệp CPP bao gồm tiêu đề nơi hàm tĩnh được khai báo sẽ có bản sao riêng của hàm, bao gồm bản sao riêng của biến ẩn toàn cầu, do đó, càng nhiều biến có các đơn vị biên dịch bao gồm tiêu đề.
Thêm "nội tuyến" vào một hàm "tĩnh" với một biến "tĩnh" bên trong:
inline static void doSomething()
{
static int value ;
}
có cùng kết quả so với việc không thêm từ khóa "nội tuyến" này, liên quan đến biến tĩnh bên trong.
Vì vậy, hành vi của VC ++ là đúng, và bạn đang nhầm ý nghĩa thực sự của "nội tuyến" và "tĩnh".