banner
cells

cells

为美好的世界献上 bug

命名空間那些坑

命名空間與源文件#

觀察以下程式碼:

// current_thread.h
#ifndef _CURRENT_THREAD_H_
#define _CURRENT_THREAD_H_

namespace mymuduolib {

namespace currentthread {

extern __thread int t_cached_tid;

void cachedTid();

} // namespace currentthread

} // namespace mymuduolib

#endif // _CURRENT_THREAD_H_

在源文件中實現頭文件中的定義,可以在實現的名稱前加上 mymuduolib::currentthread:: 前綴以指定命名空間:

// current_thread.cpp
#include "current_thread.h"
#include <unistd.h>
#include <sys/syscall.h>

__thread int mymuduolib::currentthread::t_cached_tid = 0;

void mymuduo::currentthread::cachedTid() {
    if (t_cached_tid == 0) {
        // get the current id through Linux system call
        t_cached_tid = static_cast<pid_t>(::syscall(SYS_gettid));
    }
}

如果不添加命名空間前綴,就相當於有兩個 t_cached_tidcachedTid(),一個在 mymuduo::currentthread:: 命名空間,另一個在 :: 命名空間,且 mymuduo::currentthread:: 命名空間中的定義未給出實現,如果此時在某個模組中使用 using namespace mymuduolib::currentthread; 則會調用 mymuduolib::currentthread:: 命名空間下的 cachedTid()。(當然這段程式碼如果不添加命名空間前綴是無法通過編譯的,因為函數中的變數 t_cached_tid 就會出現指定不明的問題)。

也可以將實現程式碼放在與頭文件相同的命名空間下:

// current_thread.cpp
#include "current_thread.h"
#include <unistd.h>
#include <sys/syscall.h>

namespace mymuduolib {

namespace currentthread {

__thread int t_cached_tid = 0;

void cachedTid() {
    if (t_cached_tid == 0) {
        // get the current id through Linux system call
        t_cached_tid = static_cast<pid_t>(::syscall(SYS_gettid));
    }
}
    
} // namespace mymuduolib
    
} // namespace currentthread

但是這裡不能只使用 using namespace mymuduolib::currentthread;,因為函數和變數實現的作用域需要明確指定,如未指定相當於之後的實現內容是獨立於該命名空間。

但類中成員在實現時,可以使用 using namespace 的命名空間:

// poller.h
#ifndef _POLLER_H_
#define _POLLER_H_

namespace mymuduolib {

namespace net {

class Poller {
    Poller();
};

}

} // namespace net

#endif // _POLLER_H_

在源文件中:

// poller.cpp
#include "poller.h"

// mymuduolib::net::Poller::Poller() {}

using namespace mymuduolib::net;

Poller::Poller() {}

命名空間與前置宣告#

前置宣告可以避免循環依賴的問題,當前置宣告遇到命名空間時有一個容易踩的坑。

觀察以下錯誤程式碼:

// addr.h
#ifndef _ADDR_H_
#define _ADDR_H_

namespace net {

class Addr {};

} // namespace net

#endif // _ADDR_H_
// http_connect.h
#ifndef _HTTP_CONNECT_H_
#define _HTTP_CONNECT_H_

class Addr;

namespace net {

class HttpConnect {
public:
    HttpConnect(const Addr &);
};

} // namespace net

#endif // _HTTP_CONNECT_H_
// http_connect.cpp
#include "http_connect.h"
#include "addr.h"

using namespace net;

HttpConnect::HttpConnect(const Addr &) {}
// main.cpp
#include "http_connect.h"

int main() {
    net::HttpConnect(Addr());
}

錯誤訊息:

http_connect.cpp:6:1: 錯誤:no declaration matches ‘net::HttpConnect::HttpConnect(const net::Addr&)
    6 | HttpConnect::HttpConnect(const Addr &) {}
      | ^~~~~~~~~~~
In file included from http_connect.cpp:1:
http_connect.h:8:7: 附注:备选为: ‘constexpr net::HttpConnect::HttpConnect(net::HttpConnect&&)
    8 | class HttpConnect {
      |       ^~~~~~~~~~~
http_connect.h:8:7: 附注:         ‘constexpr net::HttpConnect::HttpConnect(const net::HttpConnect&)
http_connect.h:10:5: 附注:         ‘net::HttpConnect::HttpConnect(const Addr&)
   10 |     HttpConnect(const Addr &);
      |     ^~~~~~~~~~~
http_connect.h:8:7: 附注:‘class net::HttpConnect’ defined here
    8 | class HttpConnect {
      |       ^~~~~~~~~~~

看到這個錯誤你可能會說是 using namespace 所導致的,其實並不是,使用 net::HttpConnect::HttpConnect(const net::Addr&) 也是一樣的結果。

這裡其實是前置宣告上的問題,我們需要將前置宣告 class Addr 放置 namespace net 命名空間內,因為前置宣告的 class Addr:: 作用域中是不存在的。

// http_connect.h
#ifndef _HTTP_CONNECT_H_
#define _HTTP_CONNECT_H_

namespace net {

class Addr;

class HttpConnect {
public:
    HttpConnect(const Addr &);
};

} // namespace net

#endif // _HTTP_CONNECT_H_

如有細節錯誤,歡迎指正。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。