Trong một số ứng dụng, một số hằng biểu tượng nhất định có thể cần được sử dụng trong toàn bộ code của bạn (không chỉ ở một vị trí). Chúng có thể bao gồm các hằng số vật lý hoặc toán học không thay đổi (ví dụ: số pi hoặc số Avogadro) hoặc các giá trị “điều chỉnh” dành riêng cho ứng dụng (ví dụ: hệ số ma sát hoặc trọng lực). Thay vì xác định lại các hằng số này trong mọi file cần chúng (vi phạm quy tắc “Không lặp lại chính bạn”), tốt hơn nên khai báo chúng một lần ở vị trí trung tâm và sử dụng chúng ở bất kỳ đâu cần thiết. Bằng cách đó, nếu bạn cần thay đổi chúng, bạn chỉ cần thay đổi chúng ở một nơi và những thay đổi đó có thể được phổ biến.

Bài học này thảo luận về những cách phổ biến nhất để thực hiện việc này.

1. Hằng số toàn cục làm biến nội bộ

Có nhiều cách để hỗ trợ điều này trong C ++. Trước C ++ 17, cách sau có lẽ là dễ nhất và phổ biến nhất:

1) Tạo file header để giữ các hằng số này

2) Bên trong file header này, xác định một không gian tên (được thảo luận trong bài  – Không gian tên do người dùng định nghĩa)

3) Thêm tất cả các hằng số của bạn bên trong không gian tên (đảm bảo chúng là constexpr)

4) #include file header bất cứ nơi nào bạn cần

Ví dụ:

constants.h:

#ifndef CONSTANTS_H
#define CONSTANTS_H
 
// define your own namespace to hold constants
namespace constants
{
    // constants have internal linkage by default
    constexpr double pi { 3.14159 };
    constexpr double avogadro { 6.0221413e23 };
    constexpr double my_gravity { 9.2 }; // m/s^2 -- gravity is light on this planet
    // ... other related constants
}
#endif

Sau đó, sử dụng toán tử phân giải phạm vi (: 🙂 với tên không gian tên ở bên trái và tên biến của bạn ở bên phải để truy cập các hằng số của bạn trong file .cpp:

main.cpp

/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Group: https://www.facebook.com/groups/cafedev.vn/
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
Pinterest: https://www.pinterest.com/cafedevvn/
YouTube: https://www.youtube.com/channel/UCE7zpY_SlHGEgo67pHxqIoA/
*/

#include "constants.h" // include a copy of each constant in this file
 
#include <iostream>
 
int main()
{
    std::cout << "Enter a radius: ";
    int radius{};
    std::cin >> radius;
 
    std::cout << "The circumference is: " << 2.0 * radius * constants::pi << '\n';
 
    return 0;
}

Khi tiêu đề này được #included vào file .cpp, mỗi biến trong số các biến được xác định trong tiêu đề này sẽ được sao chép vào file code đó tại điểm đưa vào. Vì các biến này nằm ngoài một hàm nên chúng được coi là các biến toàn cục trong file mà chúng được đưa vào, đó là lý do tại sao bạn có thể sử dụng chúng ở bất kỳ đâu trong file đó.

Vì const global có liên kết nội bộ, mỗi file .cpp nhận được một phiên bản độc lập của biến toàn cục mà trình liên kết không thể nhìn thấy. Trong hầu hết các trường hợp, vì đây là const, trình biên dịch sẽ chỉ cần tối ưu hóa các biến.

Như một bên …

Thuật ngữ “tối ưu hóa” đề cập đến bất kỳ quy trình nào trong đó trình biên dịch tối ưu hóa hiệu suất của chương trình của bạn bằng cách loại bỏ những thứ theo cách không ảnh hưởng đến kết quả đầu ra của chương trình của bạn. Ví dụ: giả sử bạn có một số biến const x được khởi tạo thành giá trị 4. Bất cứ khi nào code của bạn tham chiếu đến biến x, trình biên dịch chỉ có thể thay thế x bằng 4 (vì x là const, chúng ta biết nó sẽ không bao giờ thay đổi thành một giá trị khác ) và tránh phải tạo và khởi tạo một biến hoàn toàn.

2. Hằng số toàn cục làm biến bên ngoài

Phương pháp trên có một vài nhược điểm tiềm ẩn.

Mặc dù điều này đơn giản (và tốt cho các chương trình nhỏ hơn), nhưng mỗi khi constants.h được #included vào một file code khác, mỗi biến trong số này được sao chép vào file code include. Do đó, nếu constants.h được đưa vào 20 file code khác nhau, thì mỗi biến trong số này được nhân đôi 20 lần. Các trình bảo vệ header sẽ không ngăn điều này xảy ra, vì chúng chỉ ngăn header được đưa vào nhiều lần vào một file bao gồm duy nhất, không được đưa một lần vào nhiều file code khác nhau. Điều này dẫn đến hai thách thức:

1) Việc thay đổi một giá trị hằng số duy nhất sẽ yêu cầu biên dịch lại mọi file bao gồm header hằng số, điều này có thể dẫn đến thời gian xây dựng lại kéo dài cho các dự án lớn hơn.

2) Nếu các hằng số có kích thước lớn và không thể tối ưu hóa được, điều này có thể sử dụng rất nhiều bộ nhớ.

Một cách để tránh những vấn đề này là bằng cách chuyển các hằng số này thành các biến bên ngoài, vì sau đó chúng ta có thể có một biến duy nhất (được khởi tạo một lần) được chia sẻ trên tất cả các file. Trong phương pháp này, chúng ta sẽ xác định các hằng số trong file .cpp (để đảm bảo các định nghĩa chỉ tồn tại ở một nơi) và đưa ra các khai báo trong header (sẽ được các file khác đưa vào).

Bạn nên

Chúng ta sử dụng const thay vì constexpr trong phương pháp này vì các biến constexpr không thể được khai báo trước, ngay cả khi chúng có liên kết bên ngoài.

constants.cpp:

#include "constants.h"
 
namespace constants
{
    // actual global variables
    extern const double pi { 3.14159 };
    extern const double avogadro { 6.0221413e23 };
    extern const double my_gravity { 9.2 }; // m/s^2 -- gravity is light on this planet
}

constants.h:

#ifndef CONSTANTS_H
#define CONSTANTS_H
 
namespace constants
{
    // since the actual variables are inside a namespace, the forward declarations need to be inside a namespace as well
    extern const double pi;
    extern const double avogadro;
    extern const double my_gravity;
}
 
#endif

Sử dụng trong file code vẫn như cũ:

main.cpp

#include "constants.h" // include all the forward declarations
 
#include <iostream>
 
int main()
{
    std::cout << "Enter a radius: ";
    int radius{};
    std::cin >> radius;
 
    std::cout << "The circumference is: " << 2.0 * radius * constants::pi << '\n';
 
    return 0;
}

Bởi vì hằng số biểu tượng toàn cục phải được đặt trong vùng tên (để tránh xung đột đặt tên với các code định danh khác trong không gian tên chung), việc sử dụng tiền tố đặt tên “g_” là không cần thiết.

Bây giờ, các hằng biểu tượng sẽ chỉ được khởi tạo một lần (trong constants.cpp), thay vì một lần mỗi khi constants.h là #included và các cách sử dụng khác sẽ đơn giản là tham chiếu đến phiên bản trong constants.cpp. Bất kỳ thay đổi nào được thực hiện đối với constants.cpp sẽ yêu cầu biên dịch lại constants.cpp.

Tuy nhiên, có một vài nhược điểm của phương pháp này. Đầu tiên, những hằng số này hiện được coi là hằng số thời gian biên dịch chỉ trong file mà chúng thực sự được định nghĩa trong (constants.cpp), không phải bất cứ nơi nào khác mà chúng được sử dụng. Điều này có nghĩa là bên ngoài constants.cpp, chúng không thể được sử dụng ở bất kỳ đâu yêu cầu hằng số thời gian biên dịch. Thứ hai, trình biên dịch có thể không tối ưu hóa được nhiều.

Với những nhược điểm trên, bạn nên khai báo hằng số của mình trong file header. Nếu bạn thấy rằng vì lý do nào đó mà các hằng số đó đang gây ra sự cố, bạn có thể di chuyển một số hoặc tất cả chúng vào file .cpp nếu cần.

3. Hằng số toàn cục dưới dạng biến nội tuyến

C ++ 17 đã giới thiệu một khái niệm mới gọi là biến nội tuyến. Trong C ++, thuật ngữ nội tuyến đã phát triển thành “nhiều định nghĩa được cho phép”. Do đó, một biến nội tuyến là một biến được phép khai báo trong nhiều file mà không vi phạm quy tắc định nghĩa. Các biến toàn cục nội tuyến có liên kết bên ngoài theo mặc định.

Biến nội tuyến có hai hạn chế chính phải được tuân thủ:

1) Tất cả các định nghĩa của biến nội tuyến phải giống nhau (nếu không, hành vi không xác định sẽ dẫn đến).

2) Định nghĩa biến nội tuyến (không phải khai báo chuyển tiếp) phải có trong bất kỳ file nào sử dụng biến.

Trình liên kết sẽ hợp nhất tất cả các định nghĩa nội tuyến thành một định nghĩa biến duy nhất. Điều này cho phép chúng ta khai báo các biến trong file tiêu đề và xử lý chúng như thể chỉ có một định nghĩa trong file .cpp ở đâu đó. Các biến này cũng giữ lại constexpr-ness của chúng trong tất cả các file mà chúng được đưa vào.

Với điều này, chúng ta có thể quay trở lại việc xác định toàn cầu của chúng ta trong file header mà không có nhược điểm của các biến trùng lặp:

constants.h:

#ifndef CONSTANTS_H
#define CONSTANTS_H
 
// define your own namespace to hold constants
namespace constants
{
    inline constexpr double pi { 3.14159 }; // note: now inline constexpr
    inline constexpr double avogadro { 6.0221413e23 };
    inline constexpr double my_gravity { 9.2 }; // m/s^2 -- gravity is light on this planet
    // ... other related constants
}
#endif

main.cpp

#include "constants.h"
 
#include <iostream>
 
int main()
{
    std::cout << "Enter a radius: ";
    int radius{};
    std::cin >> radius;
 
    std::cout << "The circumference is: " << 2.0 * radius * constants::pi << '\n';
 
    return 0;
}

Chúng ta có thể bao gồm constants.h vào bao nhiêu file code tùy thích, nhưng những biến này sẽ chỉ được khởi tạo một lần và được chia sẻ trên tất cả các file code.

Bạn nên

Nếu bạn cần hằng số toàn cục và trình biên dịch của bạn có khả năng C ++ 17, hãy thích khai báo các biến toàn cầu constexpr nội tuyến trong file header.

Cài ứng dụng cafedev để dễ dàng cập nhật tin và học lập trình mọi lúc mọi nơi tại đây.

Nguồn và Tài liệu tiếng anh tham khảo:

Tài liệu từ cafedev:

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!