Scope & Lifetime 前言 Local Scope Global Functions & Objects

Slides:



Advertisements
Similar presentations
C++语言程序设计教程 第5章 构造数据类型 第6章 C++程序的结构.
Advertisements

第 2 章 初探 C++.
第一章 C语言概述 计算机公共教学部.
第4章 数组 数组是由一定数目的同类元素顺序排列而成的结构类型数据 一个数组在内存占有一片连续的存储区域 数组名是存储空间的首地址
第八章 类和对象.
走向C++之路 WindyWinter WindyWinter感谢诸位前来捧场。
Chapter 1 用VC++撰寫程式 Text book: Ivor Horton.
struct 可以在同一個名稱下擁有多種資料型態。使用struct能讓資料的存取和處理更為靈活。
資料大樓 --談指標與陣列 綠園.
Chap 18 類別與物件 夫有土者,有大物也。有大物者,不可以物。 物而不物,故能物物。 明乎物物者之非物也,豈獨治天下百姓而已哉!
Derived Class 前言 衍生類別的定義 單一繼承 public, protected, 和 privated 基底類別
刘胥影 东南大学计算机学院 面向对象程序设计1 2011~2012第3学期 刘胥影 东南大学计算机学院.
单片机原理与应用 C/C++在现代数字计算机上的实现.
刘胥影 东南大学计算机学院 面向对象程序设计1 2010~2011第3学期 刘胥影 东南大学计算机学院.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
刘胥影 东南大学计算机学院 面向对象程序设计1 2011~2012第3学期 刘胥影 东南大学计算机学院.
Classes: A Deeper Look, Part 1
第一章 程序的基本结构. 第一章 程序的基本结构 教材及授课结构 本章目标 基本内容 扩展阅读 上机指导 应用举例 习题.
刘胥影 东南大学计算机学院 面向对象程序设计1 2010~2011第3学期 刘胥影 东南大学计算机学院.
C++语言程序设计 C++语言程序设计 第四章 数组及自定义数据类型 C++语言程序设计.
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
Function.
Object-Oriented Programming in C++ 第一章 C++的初步知识
程序设计期末复习 黎金宁
第三章 C++中的C 面向对象程序设计(C++).
第12章 從C到C++語言 12-1 C++語言的基礎 12-2 C++語言的輸出與輸入 12-3 C++語言的動態記憶體配置
前處理指令可以要求前處理器 (preprocessor) 在程式編譯之前,先進行加入其它檔案的內容、文字取代以及選擇性編譯等工作。
第 6 章 函式.
2 C++ 的基本語法和使用環境 親自撰寫和執行程式是學好程式語言的不二法門。本章藉由兩個簡單的程式,介紹C++ 程式的基本結構和開發環境,讓初學者能逐漸建立使用C++ 的信心。
6 使用者函數 6.1 函數定義 宣告函數 呼叫函數 呼叫多個函數 6-6
类类型 C++支持的内置类型和操作,如 int i=10; i=i%6; i=i+4;
C++语言程序设计 第二章 C++简单程序设计.
程序的三种基本结构 if条件分支语句 switch多路开关语句 循环语句 循环嵌套 break,continue和goto语句
谭浩强 编著 中国高等院校计算机基础教育课程体系规划教材 C++程序设计.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
切換Dev c++顯示語言 工具->環境選項(V)->介面->language (Chinese TW)
第1章 概述 本章要点: C语言程序结构和特点 C语言程序的基本符号与关键字 C语言程序的编辑及运行 学习方法建议:
C++大学基础教程 第11章 多态性 北京科技大学 信息基础科学系 2019/4/8 北京科技大学.
Name1..hour //加班時數 name2..hour //請假時數
第二章 基本数据类型及运算 C数据类型概述 基本数据类型 运算符和表达式 混合运算与类型转换 数据的输入输出 顺序程序设计举例.
Chapter 2 & Chapter 3.
C++语言程序设计 C++语言程序设计 第五章 函数 第十一组 C++语言程序设计.
潘爱民 C++ Overview 潘爱民
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第三章 控制语句 第十一组 C++语言程序设计.
Oop8 function函式.
物件導向程式設計 CH2.
7.1 C程序的结构 7.2 作用域和作用域规则 7.3 存储属性和生存期 7.4 变量的初始化
第11章 從C到C++語言 11-1 C++語言的基礎 11-2 C++語言的資料型態與運算子 11-3 C++語言的輸出與輸入
C++语言程序设计教程 第2章 数据类型与表达式 第2章 数据类型与表达式 制作人:杨进才 沈显君.
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
C++程式設計入門 變數與運算子 作者:黃建庭.
第7章 程序的结构 四、生存期与存储属性 五、extern关键字与外部连接属性 六、static关键字与内部连接属性.
第二章 类型、对象、运算符和表达式.
C/C++基礎程式設計班 C++: 物件的使用、參考、重載函式 講師:林業峻 CSIE, NTU 3/28, 2015.
授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
第 3 章 类的基础部分 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
#include <iostream.h>
第二章 Java基本语法 讲师:复凡.
C++语言程序设计 C++语言程序设计 第二章 基本数据类型与表达式 第十一组 C++语言程序设计.
C++语言程序设计 第十章 C++标准模板库 成都信息工程学院计算机系.
C++语言程序设计 C++语言程序设计 第十章 多态 第十一组 C++语言程序设计.
本章主題 C++的程式結構 資料型態與宣告 算術運算 簡易的輸入輸出指令 程式編譯(Compile)的過程與原理.
《数据结构与算法设计》第一部分 面向对象的C++程序设计基础.
C++程序设计 概述 潘荣江 山东大学软件学院.
C++语言程序设计 C++语言程序设计 第十一章 异常处理 C++语言程序设计.
第9章 C++程序设计初步 9.1 C++的特点 9.2 最简单的C++程序 9.3 C++的输入输出 9.4 函数的重载
變數與資料型態  綠園.
C++语言程序设计 C++语言程序设计 第二章 基本数据类型与表达式 第十一组 C++语言程序设计.
Presentation transcript:

Scope & Lifetime 前言 Local Scope Global Functions & Objects Local Objects Dynamically Allocated Objects Namespace

前言 在 C++ 中,每一個變數、函式、自定的資料型態、和樣板等等都有一個名稱。這些名稱的適用範圍稱之為 scope(名域)。我們必須瞭解 scope 的規則,才能夠區別出名稱相同的物件所指涉的對象究竟是哪一個。 程式執行時,物件從生到死的期間稱之為 lifetime(生命期)。我們必須瞭解 lifetime 的規則,才能夠避免使用已經不存在的物件所導致的程式執行錯誤。

物件導向程式語言講義(Scope & Lifetime) C++ 定義了以下四種 scope: Local Scope 由函式或區塊敘述所引介的 scope。 Class Scope 由 class 定義所引介的 scope。 Namespace Scope namespace 宣告所引介的 scope。 Global Scope 上述三種之外的 scope。 靜宜大學資訊管理學系 蔡奇偉 副教授編製

// global scope int x; void g () { // local scope int y; } void f (int x) namespace N // namespace scope class C { void f (); void C::f () // class scope int y = x + 1;

在 C++ 中,任何名稱必須先宣告才能使用於算式之中。 void foo () { x = 1; // error: not declared } 名稱的可見度(visibility)始於宣告點,終於宣告所處 scope 的結尾。 int x; x = 3; // ok y = 4; // error int y; x = y + 1; // ok x 的 可見度 範圍 y 的可見度範圍

在同一層 scope 之中,名稱不可重複宣告。 void foo () void bar () { int x; … } x = 1; // error 在同一層 scope 之中,名稱不可重複宣告。 void foo () int x = 3; int x; // error x 的可見度範圍

在不同層的 scope 中,名稱可以重複宣告。 int x; void foo () { int x = 3; // ok … double x; // ok }

Name Resolution 由於名稱可以重複使用於程式之中,C++ 編譯器因而必須決定算式中的名稱到底指涉那一個宣告。這個步驟稱為 name resolution(名稱解析)。我們在這一章中先討論 local scope 的 name resolution,以後再討論 function template 定義之中和 class 定義之中的 name resolution。

void foo () { int x; x = 1; // } x = 2; // ::x = 1; // N::x = 3; // // global scope int x; 1 2 3 4 namespace N { int x; } 4 3 void bar () { x = 1; // } 1 1 2

Local Scope 函式的定義形成一個 local scope。不同的函式定義所形成的 local scope 各自獨立。比方說,在以下的兩個函式之中,變數 x 的兩個定義是獨立互不干擾。 void foo () { int x; } void bar () { int x; } 定義在 local scope 的變數稱為 local object(局部物件)。

函式之中的區塊敘述也形成一個內層的 local block scope。 local block scope 之中可以有另一個 local block scope 而形成巢狀的結構。 void foo () { int x; x = 3; } x = 2; x = 1;

定義在 if 敘述左右括號之中的物件,其有效範圍及於整個 if 敘述。 if (int x = get_value()) { x++; } x 的有效範圍 if (int x = get_value()) { x++; } else { x--; x 的有效範圍

定義在 for 敘述或 while 敘述左右括號之中的物件,其有效範圍及於整個敘述。 for (int k = 0; k < N; k++) { // ... } k 的有效範圍 while (int x = get_value()) { // ... } x 的有效範圍

測驗題: 底下的程式碼掃描輸入行以取得其中的數字串。然而其中有點錯誤,使得它無法編譯成功。請問錯誤在何處? #include <ctype.h> /* for isspace() and isdigit() */ char buf[MAX_BUFER], digits[MAX_DIGIT]; // code for getting input line into “buf” is omitted here. for (char *cp = buf; isspace(*cp); cp++) ; for (char *dp = digits; isdigit(*cp); *dp++ = *cp++) *dp = ‘\0’;

解答: C++ 編譯器會抱怨第二個 for 敘述中的 cp 變數和最後一行的 dp 變數沒有定義。正確的寫法如下: #include <ctype.h> /* for isspace() and isdigit() */ char buf[MAX_BUFER], digits[MAX_DIGIT]; // code for getting input line into “buf” is omitted here. char *cp; for (cp = buf; isspace(*cp); cp++) ; char *dp; for (dp = digits; isdigit(*cp); *dp++ = *cp++) *dp = ‘\0’;

函式參數的有效範圍涵蓋整個函式。 void foo (int x) { … } x 的有效範圍

在同一層 scope 之中,名稱不可重複定義。 void foo (int x) { int x; // error } for (int k = 1; k < N; k++) { int k; // error } void bar (int x) { int x; // ok } if (int x = get_value()) { ... } else { int x = 0; // error

當遇到一個名稱時,C++ 編譯器會由該名稱所在的 scope 開始,由內而外地搜尋對應的宣告,直到 global scope 為止。若全部找不到,則會標示為編譯錯誤。 int y; void foo () { int x; x = 2; y = 0; } x = 1; z = -1; // error: not declared

Global Functions & Objects 定義在 global scope 的函式稱為 global (全域)函式。定義在 global scope 的變數稱為 global 物件。 global 函式(除了 overloaded 函式之外)和 global 物件的定義,在整個程式中必須是惟一的,這個規則稱之為「定義惟一性」。舉例來說,假定某個程式包含 foo.cpp 和 bar.cpp 兩個檔案: // foo.cpp int x, y; void dup () { … } // bar.cpp int x; double y; void dup () { … } 它們將造成連結上的錯誤(link error)。

沒有設定初值的 global 物件在程式執行之前,其所佔據的記憶體會全部被清除成 0。但是沒有設定初值的 local 物件和 class 資料成員所佔據的記憶體則不會被清除成 0。 int x; int y = 0; ... int main () { int z; if (x == y) { cout << “x == y ” << endl; cout << “z = ” << z << endl; } else cout << “x != y” << endl; 輸出結果: x == y z = 23529970

extern 宣告 C/C++ 程式通常是由多個程式檔所組成。假定 global 函式或 global 物件並不是定義在目前的程式檔之中。如果我們想使用它們,就必須用 extern 關鍵字來宣告它們。譬如: // foo.cpp int x; int foo () { … } // bar.cpp extern int x; extern int foo (); void bar () { x = foo(); }

為了維持一致性,我們通常把 extern 宣告擺在 為了維持一致性,我們通常把 extern 宣告擺在 .h 標頭檔之中,讓需要使用的程式檔用 include 的方法加入檔中。譬如上一頁的程式可寫成以下的方式: // foo.cpp #include “foo.h” int x; int foo () { … } // bar.cpp #include “foo.h” void bar () { x = foo(); } // foo.h extern int x; extern int foo ();

雖然 global 函式和 global 物件的定義只能有一個,但是宣告卻可以重複出現,當然這些宣告必須保持一致。比方說,你不能把一個 global 物件在某一處宣告成 int,但在另外一處又宣告成 double。 // bar.cpp #include “foo.h” extern double x; extern int foo (); // ok void bar () { x = foo(); } // foo.cpp int x; int foo () { … } // foo.h extern int x; extern int foo (); 編譯器會抱怨 bar.cpp 中變數 x 的宣告不一致。

如果 global 物件的定義和 extern 宣告不一致的話,會造成以下兩種結果:(1)程式連結的錯誤、或(2)程式執行上的錯誤。比方說,編譯底下的 bar.cpp 時,變數 x 被視為一個 double 型態的外部變數。如果程式中並沒有定義這樣的 global 物件,在連結時可能會產生「變數 x 沒有定義」的錯誤訊息。即使連結時沒有發現錯誤,當程式執行時, foo.cpp 中的函式把變數 x 解釋成 int,但 bar.cpp 中的函式又把變數 x 解釋成 double。這種解釋不一致所造成的程式 bug 往往不容易追查出來 。 // bar.cpp #include “foo.h” void bar () { x = foo(); } // foo.cpp int x; int foo () { … } // foo.h extern double x; extern int foo ();

顯然地,global 函式的宣告和 extern 宣告不一致的話,也會產生一些問題。比方說, 函式 foo 的參數是單位元組的字元,但在 bar.cpp 呼叫 foo 時,卻傳遞了4 位元組的 int。 // foo.cpp void foo (char c) { … } // bar.cpp extern void foo (int) … int x; foo(x); 幸好 C++ 提供「型態安全的連結(type-safe linkage)」的機制,可用來避免這一類的問題。以上例來說,連結器(linker)會 把 bar.cpp 中的 void foo (int) 標示成一個沒有定義的函式。

所謂「型態安全的連結」是指編譯器會把函式的參數型態與個數等資訊傳送給連結器(linker),讓連結器得以檢查呼叫函式時的引數型態是否符合對應參數的宣告。這個機制主要是為了處理超載函式(overloaded functions)。

static 宣告 如果想把 global 物件或 global 函式的可見度侷限在定義的程式檔之中,我們可以在它的定義之前加上關鍵字 static。以底下的兩個程式檔為例,連結器會抱怨 bar.cpp 中的外部變數 x 和外部函式 foo() 沒有定義。 // bar.cpp extern int x; extern void foo () ; extern void setx(); void bar () { x = 1; // error foo(); // error setx(); // ok } // foo.cpp static int x; static void foo () { … } void setx () { x = 1; }

Local Objects 定義在 local scope 中的物件稱為 local object(局部物件)。根據儲存方式和生命期的不同, local objects 可區分為以下三種: automatic objects register objects local static objects

automatic objects local objects 若沒有特別地宣告,將視為 automatic objects。 這類的物件會被配置在 run-time stack 之中。譬如:底下 foo 函式中的 local objects 在 run-time stack 的配置方式類似右下圖所示。 run-time stack return address x s c f void foo (int x, short s) { char c; float f; ... }

#include <iostream> int main () { int *ip = foo(); 由於 automatic objects 是配置在 run-time stack 之中,因此只有進入函式之後,它們才真正存在,離開函式,它們就消失了。由於這樣的性質,函式的傳回值不應該是 automatic objects 的位址或參照,否則會造成程式執行錯誤。 int * foo () { int x = 10; return &x; } #include <iostream> int main () { int *ip = foo(); cout << *ip << endl; } 會產生執行錯誤

register objects 從硬體的觀點來看,CPU 內部 registers 的存取速度要比記憶體快了許多。計算所需的資料如果能夠長駐在 registers,往往能提升計算的效率。在 C/C++ 中,你可以在變數的定義之前加上關鍵字 register,要求 C++ 編譯器盡量把變數保留在 register 中,藉此來提高程式的效率。譬如迴路變數就很適合宣告成 register 。 for (register int k; k < MAX_LOOP; k++ { … }

register 宣告只是一種對 C++ 編譯器的建議,而不具有強制性。此外, C++ 編譯器的最佳化(optimization)步驟,透過對程式流程的分析,常常可以獲得 registers 配置的最好方式。基於這個緣故,目前大部份的程式設計師傾向不使用 register 宣告,而全部交由 C++ 編譯器代為決定 registers的配置方式。

local static objects 如果在一個 local object 的宣告之前加上關鍵字 static,則該物件稱為 local static object。譬如在以下的函式中,變數 count 被宣告成一個 local static object : void foo () { static int count = 0; … }

local static objects 與前兩種 local 物件不同的地方在於: 它是以 global 物件的方式來儲存,而不是存在 run-time stack之中 它的生命期並不止於 local scope 的結束,而是 延續至程式的結束。 然而 local static objects 仍是 local scope 中的物件,它的可見度還是僅涵蓋 local scope。譬如: void foo () { static int count = 0; … } void bar () { count++; // error: undefined }

local static objects 的初值設定只執行一次,而不是每一次進入函式都重新設定一遍。譬如: #include <iostream> int foo () { static int count = 0; return ++count; } void main () for (int k = 0; k < 10; k++) cout << foo(); << endl; 輸出結果: 1 2 3 . 10

Dynamically Allocated Objects 如前所述地,local 物件和 global 物件的生命期都有嚴格的規定,而無法更改。然而用動態配置的方法所產生的物件,允許我們根據程式流程的需要來控制它們的生與死。 我們可以使用 new 運算子來產生動態配置的物件,將 new 運算子傳回來的物件位址存放在指標中,然後用間接的方式來存取其中的內容。當動態配置的物件不再需要時,我們必須用 delete 運算子把它所佔據的記憶體還回給系統,才不會造成記憶體流失(memory leak)的現象。

範例: #include <iostream> int * alloc_int () { int *ip = new int(10); return ip; } int main () int *xp = alloc_int(); (*xp)++; cout << *xp << endl; delete xp; return 0; 輸出結果: 11

local 指標變數的生命期當離開所在的 scope 之後就結束了,但是它所指的動態物件若沒有被刪除掉,則會一直存在著。譬如底下 local 指標變數 ip 離開函式 alloc_int() 之後就消失,但是它所指的記憶體仍可在 main() 函式中繼續使用。 #include <iostream> int * alloc_int () { int *ip = new int(10); return ip; } int main () { int *xp = foo(); (*xp)++; cout << *xp << endl; delete xp; return 0; }

記憶體流失 當一個動態配置的物件無法用任何方法來存取其中的資料,也無法釋放它所佔據的記憶體,我們稱這個物件造成記憶體流失( memory leak )的問題。譬如在以下的例子: void foo () { int *ip = new int; } 指標 ip 所指的記憶體在離開函式 foo 之後,就無法再利用,也無法被釋放。

auto_ptr(自動指標) auto_ptr 是 C++ 標準函式庫中的一個樣板類別。它可用來自動刪除動態物件。 auto_ptr 只能用於動態配置的單一物件,而不能用於動態配置的陣列。 一個 auto_ptr 物件在宣告時,初值常被設定成一個指標(如: new 運算子所傳回來的動態物件位址)。 auto_ptr 物件可以像指標般地使用。然而,當 auto_ptr 物件的生命期結束時,它所指的動態物件會自動地刪除,而不須要使用 delete 運算子。 使用 auto_ptr 時,我們必須加入系統標頭檔:memory,如: #include <memory> using std::auto_ptr;

我們可以用以下三種方式來定義 auto_ptr 物件: auto_ptr<type_pointed_to> identifier (ptr_allocated_by_new); auto_ptr<type_pointed_to> identifier (auto_ptr_of_same_type); auto_ptr<type_pointed_to> identifier; 譬如: auto_ptr<int> api (new int(1024)); api 預設成一個整數自動指標,且 *api 的值等於 1024 auto_ptr<int> api2 (api); api2 預設成整數自動指標 api auto_ptr<int> api3 ; api3 是一個無預設值的整數自動指標

auto_ptr<int> api2 (api); int x; auto_ptr<int> api4 (&x); // error 自動指標不可用來儲存靜態變數的位址,因為靜態變數所佔據的記憶體是無法被釋放的。 auto_ptr<int> api4(new int[100]) ; // error 自動指標不可用來儲存陣列的位址

auto_ptr 物件的生命期結束時,它所指的動態物件亦自動被刪除。譬如以下的函式並不會造成記憶體流失: void foo () { auto_ptr<int> api(new int); } 原因是:當函式 foo 結束時,自動指標 api 的生命期隨之結束,經由 api 所配置的記憶體會被自動刪除。如此一來,就不會發生記憶體流失。

auto_ptr 物件會刪除它所「擁有」的動態物件,而不會刪除它所「不擁有」的動態物件。此擁有權會隨著 auto_ptr 物件間的初始化設定而移轉。當多個 auto_ptr 物件擁有同一個動態物件時,常常會因為重複刪除而造成程式執行的錯誤。 void g (T *p) { auto_ptr<T>p2(p); // p2 負責刪除 p 所指的動態物件 auto_ptr<T>p3(p2); // 擁有權從 p2 移轉給 p3,變成 // p3 負責刪除 p 所指的動態物件 auto_ptr<T>p4(p); // 程式錯誤,因為 p3 和 p4 擁有 // 相同的動態物件 }

當指定 auto_ptr 物件給另一個 auto_ptr 物件時,譬如: auto_ptr<int> p1(new int(0)); auto_ptr<int> p2(new int(1)); p1 = p2; p1 會先刪除它所擁有的動態物件,然後設定其指標指向 p2 所指的動態物件,最後把 p2 的擁有權轉移給 p1。 1 p1 p2 指定前 指定後

若 auto_ptr 物件並未設定初值的話,你可以用 type casting 的方式在稍後設定其值。譬如: auto_ptr<int> p; … p = auto_ptr<int>(new int(0)); 然而,你不可以用以下的方式: p = new int(0); // The C++ Programming Language p.reset(new int(0)); // C++ Primer p = static_cast<auto_ptr<int>>(new int(0));

auto_ptr 類別提供以下兩個成員函式: get() 此成員函式傳回 auto_ptr 物件內部的指標值。 release() 此成員函式除了傳回 auto_ptr 物件內部的指標值之外,也解除擁有權。 auto_ptr<int> p1(new int(0)); auto_ptr<int> p2(p1.get()); // error:多重擁有 auto_ptr<int> p3(p1.release()); // ok

若要測試 auto_ptr 物件內部的指標是否為空值,必須使用 get() 成員函式。譬如: auto_ptr<int> p(new int); if (p.get() != 0) { /* 指標不等於空值 */ } else { /* 指標等於空值 */ 你不能使用下列的方法: if (p != 0) { … } else { … }

namespace Namespace 的用途 Namespace 的定義與宣告 Scope operator :: Unnamed namespace 使用 namespace 中的成員 alias using 宣告 using 指令 標準 namespace: std

Namespace 的用途 若程式庫中定義了 global 物件與函式,由於它們在程式之中必須是惟一的,因此程式設計師必須避免使用到相同的名稱。當運用到的程式庫或程式檔案很多時,可能會造成程式設計師在選取名稱上的困擾。這個現象稱之為 global name space pollution。 為了解決上述的問題,C++ 提供了 namespace(名稱空間)的機制,在 global scope 加上另一層的 scope,讓程式庫設計師把它們所定義的 global 物件與函式,依據程式的邏輯架構放在不同的 namespace 之中,以避免名稱發生衝突。

// global scope int x; namespace lib { // namespace scope } int main () x = 1; lib::x = 2;

Namespace 的定義 namespace 的定義語法如下: namespace namespace_name { // global 物件與函式的定義 } 譬如: namespace lib { int x; void inverse (matrix &m) { … } double area (double radius) { … } ...

Namespace 的宣告 namespace 的宣告語法如下: namespace namespace_name { // global 物件與函式的宣告 } 譬如: namespace lib { extern int x; void inverse (matrix &); double area (double); ...

namespace 的定義(或宣告)可以分散在不同的地方。在這種情況下,這些同名的 namespace 等同於合併為一的 namespace。譬如: int x; } int y; 由於上述的合併性質以及 global 名稱必須惟一等原因,在同名的 namespace 定義之中,物件名稱和函式名稱不允許重複使用。

通常我們把 namespace 的宣告寫在. h 標頭檔內,把 namespace 的定義寫在 通常我們把 namespace 的宣告寫在 .h 標頭檔內,把 namespace 的定義寫在 .cpp 程式檔內。當須要使用此 namespace 時,則把 .h 標頭檔用 include 的方式加入程式檔之中。譬如: // lib.h namespace lib { extern int x; const double pi = 3.14; double foo (int); } // lib.cpp #include “lib.h” int x; double foo (int k) { … } // prog.cpp #include “lib.h” int x; double bar (int k) { double a = lib::pi * lib::foo(k); return x * a; }

巢狀的 namespace int x; 我們可以在一個 namespace 中再定義或宣告內層的 namespace,而形成巢狀的結構。 namespace outer { namespace inner { } int main () { x = 1; outer::x = 2; outer::inner::x = 3; 我們可以在一個 namespace 中再定義或宣告內層的 namespace,而形成巢狀的結構。

namespace_name::member_name Scope operator :: 我們可以用 scope 運算子 :: 以下列的格式: namespace_name::member_name 來存取 namespace namespace_name 中的成員 member_name。譬如: namespace lib { extern int x; // lib::x const double pi = 3.14; // lib::pi double foo (int); // lib::foo }

我們可以用 ::name 的寫法來存取 global scope 的名稱 name 。 // global scope int x; namespace lib { // namespace scope } int main () int x = 1; ::x = 2; lib::x = 3;

Unnamed namespace 若想把 global 物件或函式的可見度限制於所在的程式檔之中的話,我們可以把它們定義在所謂的「無名」namespace 之中。比方說,假定函式 swap() 只用於 sort.cpp 檔之中,而且也不希望它在其它的程式檔中被誤用。我們可以在 sort.cpp 檔之中做如下的定義: // sort.cpp namespace { // unnamed namespace void swap (int &x, int &y) { int t = x; x = y; y = t; } ...

定義在無名 namespace 之中的物件和函式,它們的可見度僅及於所在的程式檔之中。此外,我們不必使用 scope 運算子來存取它們。 int main () { int y = 1; x = 2; swap(x,y); } namespace { void swap (int &x, int &y) int t = x; x = y; y = t; int x;

C 語言原來的 static 宣告可以用無名 namespace 取代 。譬如前一頁的 C++程式範例等同於右邊所示的 C 程式。不過,寫 C++ 程式時,你應該儘量採用 C++ 的寫法。 int main () { int y = 1; x = 2; swap(x,y); } // C version static void swap (int &x, int &y) int t = x; x = y; y = t; static int x;

使用 namespace 中的成員 由於使用 scope 運算子來存取 namespace 的成員,寫起來往往非常地繁頊,因此 C++ 提供下列三種簡化的方式: alias 用一個比較短的別名來取代比較長的 namespace 名稱。 using 宣告 宣告 namespace 的成員。 using namespace 指令 宣告所使用的 namespace。

namespace short_name = long_name; 設定別名 Namespace 可以用以下的指令來設定一個比較短的別名: namespace short_name = long_name; 譬如: namespace International_Business_Machines { int x; } namespace IBM = International_Business_Machines; void foo () { IBM::x++;

巢狀內部的 namespace 也可取別名,如: namespace mylib { … namespace matrix { void foo (); } namespace libM = mylib::matrix; void bar () { libM::foo();

using namespace_name::namespace_member; 宣告之後,我們就可以直接使用 namespace_member 而不須要在其前加上 namespace_name::。 譬如: namespace mylib { void foo (); } using mylib::foo; void bar () { foo();

using 宣告也和其它名稱一樣,有其 scope 的限制。 namespace blip { int bi = 16, bj = 15, bk = 23; } int bj = 0; void mainp () { using blip::bi; ++bi; // set blip::bi to 17 using blip::bj; ++bj; // set blip::bj to 16 int bk; using blip::bk; // error: redeclaration of bk. int wrongInit = bk; // error: bk is undefined here

using namespace namespace_name; using namespce 指令 using namespace 指令的格式如下: using namespace namespace_name; 宣告之後,我們就可以直接使用名稱空間 namespace_name 中的任何成員而不須要在前面加上 namespace_name::。 譬如: namespace mylib { void foo (); } using namespace mylib; void bar () { foo();

使用 using namespace指令相當於把其中的成員置於 global scope 一樣。 namespace blip { int bi = 16, bj = 15, bk = 23; } int bj = 0; void mainp () { using namespace blip; ++bi; // set blip::bi to 17 ++bj; // error: ambiguous ++::bj; // ok, global bj ++blip::bj; // ok, blip::bj int bk = 99; ++bk; // ok: local bk. int wrongInit = bk; // error: bk is undefined here

物件導向程式語言講義(Scope & Lifetime) std namespace C++ 把系統標準函式庫的宣告和定義都擺在名為 std 的名稱空間之中。如果你在程式中 include 無 .h 副檔名的標頭檔的話,就必須使用前面所述的規則來存取 std 名稱空間的成員,否則會造成編譯上的錯誤。換句話說,你可以採取以下的方式: 使用 using namespace std 讓 std 名稱空間的成員全部暴 露在 global scope 之中。 用 using std::ios 之類的 using 宣告來挑選特定的成員。 直接用 scope 運算子來指明,如 std::cin。 靜宜大學資訊管理學系 蔡奇偉 副教授編製