在閱讀new 與delete相關的東西時,被各種命名空間全局作用域搞得有點亂,在此記錄一下。
一、命名空間的定義
只要能出現在全局作用域中的東西就可以出現在命名空間中。通常這么定義命名空間:
namespace Name {
decl and define
} // 無須分號,像代碼塊,而不是類
命名空間有下面兩個重要的屬性:
1、命名空間可以是不連續的
2、命名空間是一個獨立的作用域,所以同一個命名空間中不能出現兩個一樣的名字
所以一般我們處理自己的命名空間的方式是這樣的:
1、在自己頭文件中,先使用頭文件保護,然后定義命名空間,在命名空間中做出聲明但不定義
2、寫一份CPP文件,在文件開頭包含頭文件,同時using namespace myNamespace;(也可以分別使用域作用符), 然后定義頭文件中的聲明
值得注意的是,不能把#include<string>這樣的東西包含在自己的命名空間中,否則在自己的命名空間中定義嵌套std命名空間,就出錯了,為什么,我也不知道為什么。
二、特殊的命名空間
c++中有兩個隱式聲明的命名空間,而且非常容易混淆,它們分別是全局命名空間,與未命名的命名空間,
1,全局命名空間
全局作用域中定義的名字(即在所有的類、函數以及命名空間之外定義的名字), 都會被隱式的添加到全局命名空間中。
2, 未命名的命名空間
未命名的命名空間是指關鍵字namespace之后緊跟花括號括起來的一系列聲明語句。不同的文件都有自己獨立的命名空間,未命名的命名空間可以在一個文件中不連續,但是不會擴展到其他文件,作用相當於C語言中全局static聲明,學習C++的時候請忘掉C語言的語法。
使用未命名命名空間中的變量時,不需要添加任何域作用符,所以當全局作用域中的未命名命名空間與全局作用域中的名字一樣時,會出現二義性。
未命名的命名空間中的名字與該命名空間的作用域一樣,所以也就不難理解為什么全局作用域中的名字不能與全局未命名空間中的名字一樣了。
三、命名空間嵌套與命名空間內聯
1、 命名空間嵌套和其他的嵌套沒什么不一樣,內層屏蔽外層,在外層使用內層需要域作用符。
2、 命名空間內聯
命名空間內聯是c++ 11 的新特性,在命名空間第一次被聲明的地方加上inline關鍵字,則這個命名空間中的名字可以被外層命名空間看見,如:
namespace ns {
inline newEdition {
xxx
}
oldEdition { xx}
}
這樣用戶在使用ns命名空間中的名字時,可以直接使用新版本的名字,如ns::newName,而非內聯則需要ns::oldEdition::oldName;
通常實際的代碼會這么寫(再次聲明#include只是簡單的文本替換)
namespace ns {
inline namespace newEd { #include "newEd" }
namespace oldEd { #include "oldEd" }
}
使用命名空間中的名字:
三種方法,using 聲明,using指示,使用域作用符。
特別值得注意的是,我們在使用std::move時有一條非常重要的規則,就是一定要使用std::move, 而不是using std::move;
這里講原因:
1) using std::move;可以讓std中的move變得可見,並且作用域同該using語句,這些都沒有問題,但是在查找函數名字時,如果函數接受類類型的參數,那么編譯器還會在該類所在的命名空間去找該函數的名字,如:
std::string s;
std::cin>>s;
對於函數operator>>,由於他接受std中的string類型,所以編譯器還會在std中查找該函數,所以即使我們沒有定義自己的>>,也沒有使用using std::operator>>;編譯器還是能夠找到這個函數。
那么當我們使用using std::move;時,如果此時還有另外一個可用的move可見,如
int move(int i) { return 9; } //自定義的move using std::move; int main() { std::cout << move(1); //輸出9 return 0; }
根據函數重載時的匹配規則,非模板函數優先級高於模板函數,so,會選擇自定義的move函數。
因為這個原因,使用swap函數時,就不能使用std::swap,這樣會讓編譯器去各種地方找swap函數的生命,以選擇最好的匹配
int move(int i) { return 9; } //自定義的move int main() { using std::move; std::cout << move(1); //輸出1 return 0; }