Namespace method vs method nằm chung với hàm main()

Mình có 1 class như sau được viết trong file Common.h

class Common
{
   public:
      Common(const int& data)
      {
          this->data = data;
      }
   private:
      int data;
}

Mình có 2 file khác gọi là Namespace_Test.h và Namespace_Test.cpp
Trong Namespace_Test.h mình viết như sau:

static Common* pCommon = nullptr;
namespace CommonFunction
{
   void Init();
}

Trong Namespace_Test.cpp mình viết như sau:

void CommonFunction::Init()
{
   pCommon = new Common(10);
}

Bây giờ vào file main.cpp thì mình cũng có hàm Init() được viết như bên dưới, rồi mình viết hàm main:

void Init()
{
   pCommon = new Common(10);
}

int main()
{
   Init();
   if(pCommon == nullptr)
      cout << “Can’t initialize pCommon” << endl;
   else
      cout << “Successfully initialized pCommon” << endl;
/*
   CommonFunction::Init();
   if(pCommon == nullptr)
      cout << “Can’t initialize pCommon” << endl;
   else
      cout << “Successfully initialized pCommon” << endl;
*/
}

Vấn đề: nếu mình dùng Init() được viết trong file main.cpp thì cout sẽ in ra “Successfully initialized pCommon”. Còn nếu mình dùng Init() được định nghĩa trong namespace CommonFunction (đoạn code trong phần bị comment out trong hàm main) thì cout sẽ in ra “Can’t initialize pCommon”

Khi mình dùng debug để kiểm tra pCommon xem có bị null hay không thì thấy như hình bên dưới. Điều đó có nghĩa là cấp phát thành công. Vậy tại sao khi mình kiểm tra có null không thì nó lại thực thi đoạn code là pCommon hiện tại đang null. Tại sao lại như vậy?

PS: mình muốn biết tại sao nó lại có hiện tượng như vậy nên xin các bạn tập trung vào vấn đề đó. Xin đừng “bạn làm abc…z thì sẽ không bị vậy.” Mình đang muốn hiểu để tránh sau này chứ không phải sau này gặp lại rồi không biết tại sao.

1 Like

biến static ở file scope thì mỗi translation unit sẽ chứa 1 biến riêng, chỉ thuộc về unit đó, ko phải là tất cả translation unit xài chung 1 biến

khai báo

static Common* pCommon = nullptr;

ở file Namespace_Test.h kia thì trong Namespace_Test.cpp có include Namespace_Test.h, sẽ có 1 biến pCommon riêng thuộc về unit tạm gọi là Namespace_Test.cpp. File main.cpp cũng include Namespace_Test.hpp, và cũng tạo ra 1 biến pCommon riêng thuộc về main.cpp. Thành ra có 2 biến pCommon, khi gọi CommonFunction::Init() chỉ init pCommon của Namespace_Test.cpp, còn pCommon của main.cpp vẫn ko đổi.


muốn chỉ có 1 pCommon thì phải khai báo nó là extern trong file .h kia :V
Namespace_Test.hpp

extern Common* pCommon;

và ở 1 file .cpp nào đó, khai báo pCommon nhưng ko phải là static :V Nếu khai báo static nữa thì chỉ file .cpp đó biết được pCommon thì phải :V Ở đây cho vào Namespace_Test.cpp có lẽ hợp lý nhất:
Namespace_Test.cpp

#include ...

Common* pCommon = nullptr;

void CommonFunction::Init()
...

bổ sung thêm là C++17 có keyword inline cho phép chuyển biến toàn cục thành external linkage như khai báo extern + 1 biến toàn cục ko có specifier ở đâu đó. Với cái này chỉ cần sửa chữ static thành inline là xong :V rất tiện khi viết thư viện header-only.

https://en.cppreference.com/w/cpp/language/inline

vậy là biến toàn cục từ ko có specifier, có static, có extern nay lại thêm inline nữa :V :V


thêm mấy cái hình vẽ thử bằng Mermaid’s classDiagram (dùng cho UML ở đây xài ké =))

Lỗi định nghĩa biến pCommon đã được định nghĩa trước đó do khai báo biến toàn cục ko có specifier:
image


2 biến pCommon thuộc về 2 translation units khác nhau do khai báo static:
image


1 biến pCommon nằm trong Namespace_Test_o, được main_o biết đến nhờ extern
image


C++17 inline variable
image

6 Likes

Mình hiểu rồi. Cám ơn bạn.
Cho mình hỏi thêm là Init() trong main.cpp ưu tiên cấp phát cho pCommon của translation unit main.cpp còn CommonFunction::Init() thì gọi pCommon trong cái translation unit của nó. Như vậy có thể hiểu như là 1 kiểu variablw shadowing không?

cũng ko hẳn là shadow :V Local x shadow global x nghĩa là local x được ưu tiên hơn global x, tức là hàm gọi shadowed x biết có 2 biến x cùng tên nhưng nó ưu tiên chọn biến local. Còn ở đây Common::Init() chỉ biết có 1 biến pCommon thì nó shadow biến nào :V Trong file chứa definition của Common::Init() thì nó đâu có shadow biến nào, sao gọi là shadow được :V

3 Likes

Ai mượn khai báo biến trong file .h rồi include loạn xà ngầu lên làm chi cho khó hiểu rối tung lên!?
Đã ko hiểu bản chất về scope của biến, còn code dị. → Gọi là “tự tay bóp …ái”

2 Likes

Đã nói là muốn biết tìm hiểu bản chất vấn đề rồi mà bạn vẫn comment vui ghê. Không biết thì hỏi. Có làm vô project thực tế đâu. Tôi không làm vậy nhưng có chắc sau này ko gặp legacy code của người khác viết như vậy không? Ngồi vọc chơi mà cũng có người như bạn vô nói.

1 Like

mình rõ rồi. Cám ơn bạn

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?