Để làm rõ câu hỏi, tôi muốn phân loại việc sử dụng từ khóa 'tĩnh' theo ba hình thức khác nhau:
(A). biến
(B). chức năng
(C). biến thành viên / chức năng của lớp
giải thích sau đây cho mỗi tiêu đề phụ:
(A) từ khóa 'tĩnh' cho các biến
Điều này có thể hơi khó khăn tuy nhiên nếu được giải thích và hiểu đúng, nó khá đơn giản.
Để giải thích điều này, đầu tiên thực sự hữu ích khi biết về phạm vi, thời lượng và mối liên kết của các biến, mà không có điều gì luôn khó thấy thông qua khái niệm mờ ám của từ khóa cổ
1. Phạm vi : Xác định vị trí trong tệp, biến có thể truy cập được. Nó có thể có hai loại: (i) Phạm vi cục bộ hoặc Khối . (ii) Phạm vi toàn cầu
2. Thời lượng : Xác định khi một biến được tạo và hủy. Một lần nữa, nó có hai loại: (i) Thời lượng lưu trữ tự động (đối với các biến có phạm vi cục bộ hoặc Khối). (ii) Thời lượng lưu trữ tĩnh (đối với các biến có Phạm vi toàn cầu hoặc biến cục bộ (trong hàm hoặc khối mã) với chỉ định tĩnh ).
3. Liên kết : Xác định xem một biến có thể được truy cập (hoặc liên kết) trong một tệp khác. Một lần nữa (và may mắn thay) có hai loại: (i) Liên kết nội bộ
(đối với các biến có Phạm vi khối và Phạm vi toàn cầu / Phạm vi tệp / Phạm vi không gian toàn cầu) (ii) Liên kết ngoài (đối với các biến chỉ dành cho Phạm vi toàn cầu / Phạm vi tệp / Phạm vi không gian tên toàn cầu)
Hãy tham khảo một ví dụ dưới đây để hiểu rõ hơn về các biến toàn cục và biến cục bộ (không có biến cục bộ nào có thời lượng lưu trữ tĩnh):
//main file
#include <iostream>
int global_var1; //has global scope
const global_var2(1.618); //has global scope
int main()
{
//these variables are local to the block main.
//they have automatic duration, i.e, they are created when the main() is
// executed and destroyed, when main goes out of scope
int local_var1(23);
const double local_var2(3.14);
{
/* this is yet another block, all variables declared within this block are
have local scope limited within this block. */
// all variables declared within this block too have automatic duration, i.e,
/*they are created at the point of definition within this block,
and destroyed as soon as this block ends */
char block_char1;
int local_var1(32) //NOTE: this has been re-declared within the block,
//it shadows the local_var1 declared outside
std::cout << local_var1 <<"\n"; //prints 32
}//end of block
//local_var1 declared inside goes out of scope
std::cout << local_var1 << "\n"; //prints 23
global_var1 = 29; //global_var1 has been declared outside main (global scope)
std::cout << global_var1 << "\n"; //prints 29
std::cout << global_var2 << "\n"; //prints 1.618
return 0;
} //local_var1, local_var2 go out of scope as main ends
//global_var1, global_var2 go out of scope as the program terminates
//(in this case program ends with end of main, so both local and global
//variable go out of scope together
Bây giờ đến khái niệm Liên kết. Khi một biến toàn cục được xác định trong một tệp được dự định sẽ được sử dụng trong một tệp khác, liên kết của biến đó đóng một vai trò quan trọng.
Liên kết của các biến toàn cục được chỉ định bởi các từ khóa: (i) tĩnh và, (ii) extern
(Bây giờ bạn có được lời giải thích)
từ khóa tĩnh có thể được áp dụng cho các biến có phạm vi cục bộ và toàn cầu, và trong cả hai trường hợp, chúng có nghĩa là những thứ khác nhau. Trước tiên tôi sẽ giải thích việc sử dụng từ khóa 'tĩnh' trong các biến có phạm vi toàn cầu (trong đó tôi cũng làm rõ cách sử dụng từ khóa 'extern') và sau đó cho những người có phạm vi địa phương.
1. Từ khóa tĩnh cho các biến có phạm vi toàn cầu
Các biến toàn cục có thời lượng tĩnh, nghĩa là chúng không đi ra khỏi phạm vi khi một khối mã cụ thể (ví dụ: main ()) trong đó nó được sử dụng kết thúc. Tùy thuộc vào liên kết, chúng chỉ có thể được truy cập trong cùng một tệp nơi chúng được khai báo (đối với biến toàn cục tĩnh) hoặc bên ngoài tệp ngay cả bên ngoài tệp mà chúng được khai báo (biến toàn cục kiểu bên ngoài)
Trong trường hợp biến toàn cục có bộ xác định bên ngoài và nếu biến này được truy cập bên ngoài tệp đã được khởi tạo, thì nó phải được chuyển tiếp khai báo trong tệp nơi nó được sử dụng, giống như một hàm phải được chuyển tiếp tuyên bố nếu định nghĩa của nó là trong một tệp khác với nơi nó được sử dụng.
Ngược lại, nếu biến toàn cục có từ khóa tĩnh, nó không thể được sử dụng trong một tệp bên ngoài nó đã được khai báo.
(xem ví dụ dưới đây để làm rõ)
ví dụ:
//main2.cpp
static int global_var3 = 23; /*static global variable, cannot be
accessed in anyother file */
extern double global_var4 = 71; /*can be accessed outside this file linked to main2.cpp */
int main() { return 0; }
main3.cpp
//main3.cpp
#include <iostream>
int main()
{
extern int gloabl_var4; /*this variable refers to the gloabal_var4
defined in the main2.cpp file */
std::cout << global_var4 << "\n"; //prints 71;
return 0;
}
bây giờ bất kỳ biến nào trong c ++ đều có thể là const hoặc không phải const và với mỗi 'const-ness', chúng ta có hai trường hợp liên kết c ++ mặc định, trong trường hợp không có chỉ định nào:
(i) Nếu một biến toàn cục không phải là const, mặc định liên kết của nó là extern , nghĩa là biến toàn cục không const có thể được truy cập trong một tệp .cpp khác bằng cách khai báo chuyển tiếp bằng cách sử dụng từ khóa extern (nói cách khác, không phải là toàn cầu các biến có liên kết bên ngoài (với thời lượng tĩnh của khóa học)). Ngoài ra việc sử dụng từ khóa extern trong tệp gốc nơi nó đã được xác định là không cần thiết. Trong trường hợp này để làm cho một biến toàn cục không const không thể truy cập được vào tệp bên ngoài, hãy sử dụng bộ xác định 'tĩnh' trước loại biến .
(ii) Nếu một biến toàn cục là const, liên kết của nó là tĩnh theo mặc định , nghĩa là một biến toàn cục const không thể được truy cập trong một tệp khác với nơi được định nghĩa, (nói cách khác, các biến toàn cục có liên kết bên trong (với thời lượng tĩnh tất nhiên)). Ngoài ra việc sử dụng từ khóa tĩnh để ngăn chặn một biến toàn cầu const được truy cập trong một tệp khác là không cần thiết. Ở đây, để tạo một biến toàn cục const có một liên kết ngoài, hãy sử dụng bộ xác định 'extern' trước kiểu của biến
Dưới đây là tóm tắt về các biến phạm vi toàn cầu với các liên kết khác nhau
//globalVariables1.cpp
// defining uninitialized vairbles
int globalVar1; // uninitialized global variable with external linkage
static int globalVar2; // uninitialized global variable with internal linkage
const int globalVar3; // error, since const variables must be initialized upon declaration
const int globalVar4 = 23; //correct, but with static linkage (cannot be accessed outside the file where it has been declared*/
extern const double globalVar5 = 1.57; //this const variable ca be accessed outside the file where it has been declared
Tiếp theo, chúng tôi điều tra cách các biến toàn cục ở trên hoạt động khi được truy cập trong một tệp khác.
//using_globalVariables1.cpp (eg for the usage of global variables above)
// Forward declaration via extern keyword:
extern int globalVar1; // correct since globalVar1 is not a const or static
extern int globalVar2; //incorrect since globalVar2 has internal linkage
extern const int globalVar4; /* incorrect since globalVar4 has no extern
specifier, limited to internal linkage by
default (static specifier for const variables) */
extern const double globalVar5; /*correct since in the previous file, it
has extern specifier, no need to initialize the
const variable here, since it has already been
legitimately defined perviously */
2. Từ khóa tĩnh cho các biến có Phạm vi cục bộ
Cập nhật (tháng 8 năm 2019) về từ khóa tĩnh cho các biến trong phạm vi cục bộ
Điều này hơn nữa có thể được chia thành hai loại:
(i) từ khóa tĩnh cho các biến trong một khối chức năng và (ii) từ khóa tĩnh cho các biến trong một khối cục bộ không tên.
(i) từ khóa tĩnh cho các biến trong một khối chức năng.
Trước đó, tôi đã đề cập rằng các biến có phạm vi cục bộ có thời lượng tự động, nghĩa là chúng tồn tại khi khối được nhập (có thể là khối bình thường, có thể là khối chức năng) và ngừng tồn tại khi khối kết thúc, câu chuyện dài, biến ngắn với phạm vi cục bộ có thời lượng tự động và các biến thời lượng tự động (và các đối tượng) không có liên kết có nghĩa là chúng không hiển thị bên ngoài khối mã.
Nếu bộ xác định tĩnh được áp dụng cho một biến cục bộ trong một khối chức năng, nó sẽ thay đổi thời lượng của biến từ tự động sang tĩnh và thời gian tồn tại của nó là toàn bộ thời lượng của chương trình, có nghĩa là nó có một vị trí bộ nhớ cố định và giá trị của nó chỉ được khởi tạo một lần trước khi khởi động chương trình như đã đề cập trong tài liệu tham khảo cpp (không nên nhầm lẫn với việc khởi tạo
Hãy xem một ví dụ.
//localVarDemo1.cpp
int localNextID()
{
int tempID = 1; //tempID created here
return tempID++; //copy of tempID returned and tempID incremented to 2
} //tempID destroyed here, hence value of tempID lost
int newNextID()
{
static int newID = 0;//newID has static duration, with internal linkage
return newID++; //copy of newID returned and newID incremented by 1
} //newID doesn't get destroyed here :-)
int main()
{
int employeeID1 = localNextID(); //employeeID1 = 1
int employeeID2 = localNextID(); // employeeID2 = 1 again (not desired)
int employeeID3 = newNextID(); //employeeID3 = 0;
int employeeID4 = newNextID(); //employeeID4 = 1;
int employeeID5 = newNextID(); //employeeID5 = 2;
return 0;
}
Nhìn vào tiêu chí trên cho các biến cục bộ tĩnh và biến toàn cục tĩnh, người ta có thể muốn hỏi, sự khác biệt giữa chúng có thể là gì. Trong khi các biến toàn cục có thể truy cập bất cứ lúc nào trong vòng mã (trong cùng một cũng như đơn vị dịch thuật khác nhau tùy thuộc vào const -ness và extern -ness), một biến tĩnh được định nghĩa trong một khối chức năng là không trực tiếp tiếp cận. Biến phải được trả về bởi giá trị hàm hoặc tham chiếu. Hãy chứng minh điều này bằng một ví dụ:
//localVarDemo2.cpp
//static storage duration with global scope
//note this variable can be accessed from outside the file
//in a different compilation unit by using `extern` specifier
//which might not be desirable for certain use case.
static int globalId = 0;
int newNextID()
{
static int newID = 0;//newID has static duration, with internal linkage
return newID++; //copy of newID returned and newID incremented by 1
} //newID doesn't get destroyed here
int main()
{
//since globalId is accessible we use it directly
const int globalEmployee1Id = globalId++; //globalEmployeeId1 = 0;
const int globalEmployee2Id = globalId++; //globalEmployeeId1 = 1;
//const int employeeID1 = newID++; //this will lead to compilation error since newID++ is not accessible direcly.
int employeeID2 = newNextID(); //employeeID3 = 0;
int employeeID2 = newNextID(); //employeeID3 = 1;
return 0;
}
Giải thích thêm về sự lựa chọn của biến cục bộ tĩnh và biến cục bộ tĩnh có thể được tìm thấy trên luồng stackoverflow này
(ii) từ khóa tĩnh cho các biến trong một khối cục bộ không tên.
các biến tĩnh trong một khối cục bộ (không phải là một khối chức năng) không thể được truy cập bên ngoài khối một khi khối cục bộ nằm ngoài phạm vi. Không cẩn thận với quy tắc này.
//localVarDemo3.cpp
int main()
{
{
const static int static_local_scoped_variable {99};
}//static_local_scoped_variable goes out of scope
//the line below causes compilation error
//do_something is an arbitrary function
do_something(static_local_scoped_variable);
return 0;
}
C ++ 11 đã giới thiệu từ khóa constexpr
đảm bảo đánh giá biểu thức tại thời điểm biên dịch và cho phép trình biên dịch tối ưu hóa mã. Bây giờ nếu giá trị của biến const tĩnh trong phạm vi được biết tại thời điểm biên dịch, mã được tối ưu hóa theo cách tương tự với biến với constexpr
. Đây là một ví dụ nhỏ
Tôi cũng khuyên độc giả nên tìm kiếm sự khác biệt giữa constexpr
và static const
cho các biến trong luồng stackoverflow này . điều này kết luận lời giải thích của tôi cho từ khóa tĩnh được áp dụng cho các biến.
Từ khóa B. 'tĩnh' được sử dụng cho các chức năng
về mặt chức năng, từ khóa tĩnh có nghĩa đơn giản. Ở đây, nó đề cập đến liên kết của hàm
Thông thường tất cả các hàm được khai báo trong tệp cpp đều có liên kết ngoài theo mặc định, nghĩa là một hàm được xác định trong một tệp có thể được sử dụng trong tệp cpp khác bằng cách khai báo chuyển tiếp.
sử dụng một từ khóa tĩnh trước khi khai báo hàm giới hạn liên kết của nó thành nội bộ , tức là một hàm tĩnh có thể được sử dụng trong một tệp nằm ngoài định nghĩa của nó.
C. Staitc Keyword được sử dụng cho các biến thành viên và chức năng của các lớp
1. từ khóa 'tĩnh' cho các biến thành viên của các lớp
Tôi bắt đầu trực tiếp với một ví dụ ở đây
#include <iostream>
class DesignNumber
{
private:
static int m_designNum; //design number
int m_iteration; // number of iterations performed for the design
public:
DesignNumber() { } //default constructor
int getItrNum() //get the iteration number of design
{
m_iteration = m_designNum++;
return m_iteration;
}
static int m_anyNumber; //public static variable
};
int DesignNumber::m_designNum = 0; // starting with design id = 0
// note : no need of static keyword here
//causes compiler error if static keyword used
int DesignNumber::m_anyNumber = 99; /* initialization of inclass public
static member */
enter code here
int main()
{
DesignNumber firstDesign, secondDesign, thirdDesign;
std::cout << firstDesign.getItrNum() << "\n"; //prints 0
std::cout << secondDesign.getItrNum() << "\n"; //prints 1
std::cout << thirdDesign.getItrNum() << "\n"; //prints 2
std::cout << DesignNumber::m_anyNumber++ << "\n"; /* no object
associated with m_anyNumber */
std::cout << DesignNumber::m_anyNumber++ << "\n"; //prints 100
std::cout << DesignNumber::m_anyNumber++ << "\n"; //prints 101
return 0;
}
Trong ví dụ này, biến tĩnh m_designNum giữ nguyên giá trị của nó và biến thành viên riêng lẻ này (vì nó tĩnh) được chia sẻ b / w tất cả các biến của kiểu đối tượng DesignNumber
Cũng giống như các biến thành viên khác, các biến thành viên tĩnh của một lớp không được liên kết với bất kỳ đối tượng lớp nào, điều này được thể hiện bằng cách in anyNumber trong hàm chính
const vs non-const biến thành viên tĩnh trong lớp
(i) các biến thành viên tĩnh không phải là lớp const
Trong ví dụ trước, các thành viên tĩnh (cả công khai và riêng tư) là các hằng số. Tiêu chuẩn ISO cấm các thành viên tĩnh không const được khởi tạo trong lớp. Do đó, như trong ví dụ trước, chúng phải được kích hoạt sau định nghĩa lớp, với lời cảnh báo rằng từ khóa tĩnh cần phải được bỏ qua
(ii) các biến thành viên const-static của lớp
này rất đơn giản và đi theo quy ước khởi tạo biến thành viên const khác, tức là các biến thành viên tĩnh của một lớp có thể được khởi tạo tại điểm khai báo và chúng có thể được khởi tạo ở cuối của khai báo lớp với một cảnh báo rằng từ khóa const cần được thêm vào thành viên tĩnh khi được khởi tạo sau định nghĩa lớp.
Tuy nhiên, tôi khuyên bạn nên khởi tạo các biến thành viên tĩnh tại điểm khai báo. Điều này đi với quy ước C ++ tiêu chuẩn và làm cho mã trông gọn gàng hơn
để biết thêm ví dụ về các biến thành viên tĩnh trong một lớp, hãy tìm liên kết sau từ learncpp.com
http://www.learncpp.com/cpp-tutorial/811-static-member-variabled/
2. Từ khóa 'tĩnh' cho chức năng thành viên của các lớp
Giống như các biến thành viên của các lớp có thể, là tĩnh, các hàm thành viên của các lớp cũng có thể. Các hàm thành viên bình thường của các lớp luôn được liên kết với một đối tượng của kiểu lớp. Ngược lại, các hàm thành viên tĩnh của một lớp không được liên kết với bất kỳ đối tượng nào của lớp, tức là chúng không có * con trỏ này.
Thứ hai, vì các hàm thành viên tĩnh của lớp không có * con trỏ này, nên chúng có thể được gọi bằng cách sử dụng tên lớp và toán tử phân giải phạm vi trong hàm chính (ClassName :: functionName ();)
Các hàm thành viên tĩnh thứ ba của một lớp chỉ có thể truy cập các biến thành viên tĩnh của một lớp, vì các biến thành viên không tĩnh của một lớp phải thuộc về một đối tượng lớp.
để biết thêm ví dụ về các hàm thành viên tĩnh trong một lớp, hãy tìm liên kết sau từ learncpp.com
http://www.learncpp.com/cpp-tutorial/812-static-member-fifts/