挑战一个月根本把握C++(第十二天)了解定名空间,模板,预处置惩罚器 ...

打印 上一主题 下一主题

主题 818|帖子 818|积分 2454

一 定名空间

假设这样一种情况,当一个班上有两个名叫 Zara 的学生时,为了明确区分它们,我们在使用名字之外,不得不使用一些额外的信息,好比他们的家庭住址,大概他们父母的名字等等。
同样的情况也出如今 C++ 应用程序中。例如,您可能会写一个名为 xyz() 的函数,在另一个可用的库中也存在一个雷同的函数 xyz()。这样,编译器就无法判断您所使用的是哪一个 xyz() 函数。
因此,引入了定名空间这个概念,专门用于办理上面的题目,它可作为附加信息来区分不同库中雷同名称的函数、类、变量等。使用了定名空间即界说了上下文。本质上,定名空间就是界说了一个范围。
我们举一个盘算机系统中的例子,一个文件夹(目录)中可以包罗多个文件夹,每个文件夹中不能有雷同的文件名,但不同文件夹中的文件可以重名。

 1.1 界说定名空间

定名空间的界说使用关键字 namespace,后跟定名空间的名称,如下所示:
  1. namespace namespace_name {
  2.    // 代码声明
  3. }
复制代码
为了调用带有定名空间的函数或变量,须要在前面加上定名空间的名称,如下所示:
  1. name::code;  // code 可以是变量或函数
复制代码
定名空间如何为变量或函数等实体界说范围:
  1. #include <iostream>
  2. using namespace std;
  3. // 第一个命名空间
  4. namespace first_space{
  5.    void func(){
  6.       cout << "Inside first_space
  7. " << endl;
  8.    }
  9. }
  10. // 第二个命名空间
  11. namespace second_space{
  12.    void func(){
  13.       cout << "Inside second_space
  14. " << endl;
  15.    }
  16. }
  17. int main ()
  18. {
  19.    // 调用第一个命名空间中的函数
  20.    first_space::func();
  21.    
  22.    // 调用第二个命名空间中的函数
  23.    second_space::func();
  24.    return 0;
  25. }
复制代码
输出:
  1. Inside first_space
  2. Inside second_space
复制代码
1.2 using 指令

您可以使用 using namespace 指令,这样在使用定名空间时就可以不消在前面加上定名空间的名称。这个指令会告诉编译器,后续的代码将使用指定的定名空间中的名称。
  1. #include <iostream>
  2. using namespace std;
  3. // 第一个命名空间
  4. namespace first_space{
  5.    void func(){
  6.       cout << "Inside first_space
  7. " << endl;
  8.    }
  9. }
  10. // 第二个命名空间
  11. namespace second_space{
  12.    void func(){
  13.       cout << "Inside second_space
  14. " << endl;
  15.    }
  16. }
  17. using namespace first_space;
  18. int main ()
  19. {
  20.    // 调用第一个命名空间中的函数
  21.    func();
  22.    
  23.    return 0;
  24. }
复制代码
输出:
  1. Inside first_space
复制代码
using 指令也可以用来指定定名空间中的特定项目。例如,假如您只打算使用 std 定名空间中的 cout 部门:
  1. using std::cout;
复制代码
随后的代码中,在使用 cout 时就可以不消加上定名空间名称作为前缀,但是 std 定名空间中的其他项目仍旧须要加上定名空间名称作为前缀,如下所示:
  1. #include <iostream>using std::cout;
  2. int main (){    cout << "std::endl is used with std!
  3. " << std::endl;      return 0;}
复制代码
输出:
  1. std::endl is used with std!
复制代码
using 指令引入的名称遵照正常的范围规则。名称从使用 using 指令开始是可见的,直到该范围结束。此时,在范围以外界说的同名实体是隐藏的。
1.3 不连续的定名空间

定名空间可以界说在几个不同的部门中,因此定名空间是由几个单独界说的部门组成的。一个定名空间的各个组成部门可以分散在多个文件中。
以是,假如定名空间中的某个组成部门须要请求界说在另一个文件中的名称,则仍旧须要声明该名称。下面的定名空间界说可以是界说一个新的定名空间,也可以是为已有的定名空间增加新的元素:
  1. namespace namespace_name {
  2.    // 代码声明
  3. }
复制代码
1.4 嵌套的定名空间

定名空间可以嵌套,您可以在一个定名空间中界说另一个定名空间,如下所示:
  1. namespace namespace_name1 {
  2.    // 代码声明
  3.    namespace namespace_name2 {
  4.       // 代码声明
  5.    }
  6. }
复制代码
可以通过使用 :: 运算符来访问嵌套的定名空间中的成员:
  1. // 访问 namespace_name2 中的成员
  2. using namespace namespace_name1::namespace_name2;
  3. // 访问 namespace_name1 中的成员
  4. using namespace namespace_name1;
复制代码
在上面的语句中,假如使用的是 namespace_name1,那么在该范围内 namespace_name2 中的元素也是可用的,如下所示:
  1. #include <iostream>using namespace std; // 第一个定名空间namespace first_space{   void func(){      cout << "Inside first_space
  2. " << endl;   }   // 第二个定名空间   namespace second_space{      void func(){         cout << "Inside second_space
  3. " << endl;      }   }}using namespace first_space::second_space;int main (){    // 调用第二个定名空间中的函数   func();      return 0;}
复制代码
输出:
  1. Inside second_space
复制代码
二 模板

模板是泛型编程的基础,泛型编程即以一种独立于任何特定范例的方式编写代码。
模板是创建泛型类或函数的蓝图或公式。库容器,好比迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。
每个容器都有一个单一的界说,好比 向量,我们可以界说许多不同范例的向量,好比 vector <int> 或 vector <string>
可以使用模板来界说函数和类,接下来让我们一起来看看如何使用。
2.1 函数模板

模板函数界说的一般形式如下所示:
  1. template <typename type> ret-type func-name(parameter list)
  2. {
  3.    // 函数的主体
  4. }
复制代码
在这里,type 是函数所使用的数据范例的占位符名称。这个名称可以在函数界说中使用。
下面是函数模板的实例,返回两个数中的最大值:
  1. #include <iostream>
  2. #include <string>
  3. using namespace std;
  4. template <typename T>
  5. inline T const& Max (T const& a, T const& b)
  6. {
  7.     return a < b ? b:a;
  8. }
  9. int main ()
  10. {
  11.     int i = 39;
  12.     int j = 20;
  13.     cout << "Max(i, j): " << Max(i, j) << endl;
  14.     double f1 = 13.5;
  15.     double f2 = 20.7;
  16.     cout << "Max(f1, f2): " << Max(f1, f2) << endl;
  17.     string s1 = "Hello";
  18.     string s2 = "World";
  19.     cout << "Max(s1, s2): " << Max(s1, s2) << endl;
  20.     return 0;
  21. }
复制代码
输出:
  1. Max(i, j): 39
  2. Max(f1, f2): 20.7
  3. Max(s1, s2): World
复制代码
2.2 类模板

正如我们界说函数模板一样,我们也可以界说类模板。泛型类声明的一般形式如下所示:
  1. template <class type> class class-name {
  2. .
  3. .
  4. .
  5. }
复制代码
在这里,type 是占位符范例名称,可以在类被实例化的时间进行指定。您可以使用一个逗号分隔的列表来界说多个泛型数据范例。
下面的实例界说了类 Stack<>,并实现了泛型方法来对元素进行入栈出栈操纵:
  1. #include <iostream>
  2. #include <vector>
  3. #include <cstdlib>
  4. #include <string>
  5. #include <stdexcept>
  6. using namespace std;
  7. template <class T>
  8. class Stack {
  9.   private:
  10.     vector<T> elems;     // 元素
  11.   public:
  12.     void push(T const&);  // 入栈
  13.     void pop();               // 出栈
  14.     T top() const;            // 返回栈顶元素
  15.     bool empty() const{       // 如果为空则返回真。
  16.         return elems.empty();
  17.     }
  18. };
  19. template <class T>
  20. void Stack<T>::push (T const& elem)
  21. {
  22.     // 追加传入元素的副本
  23.     elems.push_back(elem);   
  24. }
  25. template <class T>
  26. void Stack<T>::pop ()
  27. {
  28.     if (elems.empty()) {
  29.         throw out_of_range("Stack<>::pop(): empty stack");
  30.     }
  31.     // 删除最后一个元素
  32.     elems.pop_back();         
  33. }
  34. template <class T>
  35. T Stack<T>::top () const
  36. {
  37.     if (elems.empty()) {
  38.         throw out_of_range("Stack<>::top(): empty stack");
  39.     }
  40.     // 返回最后一个元素的副本
  41.     return elems.back();      
  42. }
  43. int main()
  44. {
  45.     try {
  46.         Stack<int>         intStack;  // int 类型的栈
  47.         Stack<string> stringStack;    // string 类型的栈
  48.         // 操作 int 类型的栈
  49.         intStack.push(7);
  50.         cout << intStack.top() <<endl;
  51.         // 操作 string 类型的栈
  52.         stringStack.push("hello");
  53.         cout << stringStack.top() << std::endl;
  54.         stringStack.pop();
  55.         stringStack.pop();
  56.     }
  57.     catch (exception const& ex) {
  58.         cerr << "Exception: " << ex.what() <<endl;
  59.         return -1;
  60.     }
  61. }
复制代码
输出:
  1. 7
  2. hello
  3. Exception: Stack<>::pop(): empty stack
复制代码
三 预处置惩罚器

预处置惩罚器是一些指令,指示编译器在实际编译之前所需完成的预处置惩罚。
所有的预处置惩罚器指令都是以井号(#)开头,只有空格字符可以出如今预处置惩罚指令之前。预处置惩罚指令不是 C++ 语句,以是它们不会以分号(;)结尾。
我们已经看到,之前所有的实例中都有 #include 指令。这个宏用于把头文件包罗到源文件中。
C++ 还支持很多预处置惩罚指令,好比 #include、#define、#if、#else、#line 等,让我们一起看看这些重要指令。
3.1 #define 预处置惩罚

#define 预处置惩罚指令用于创建符号常量。该符号常量通常称为,指令的一般形式是:
  1. #define macro-name replacement-text
复制代码
当这一行代码出如今一个文件中时,在该文件中后续出现的所有宏都将会在程序编译之前被替换为 replacement-text。例如:
  1. #include <iostream>
  2. using namespace std;
  3. #define PI 3.14159
  4. int main ()
  5. {
  6.     cout << "Value of PI :" << PI << endl;
  7.     return 0;
  8. }
复制代码
如今,让我们测试这段代码,看看预处置惩罚的结果。假设源代码文件已经存在,接下来使用 -E 选项进行编译,并把结果重定向到 test.p。如今,假如您查看 test.p 文件,将会看到它已经包罗大量的信息,而且在文件底部的值被改为如下:
  1. $ gcc -E test.cpp > test.p
  2. ...
  3. int main ()
  4. {
  5.     cout << "Value of PI :" << 3.14159 << endl;
  6.     return 0;
  7. }
复制代码
3.2 参数宏

可以使用 #define 来界说一个带有参数的宏,如下所示:
  1. #include <iostream>
  2. using namespace std;
  3. #define MIN(a,b) (a<b ? a : b)
  4. int main ()
  5. {
  6.    int i, j;
  7.    i = 100
  8. ;
  9.    j = 30;
  10.    cout <<"较小的值为:" << MIN(i, j) << endl;
  11.     return 0;
  12. }
复制代码
输出:
  1. 较小的值为:30
复制代码
3.3 条件编译

有几个指令可以用来有选择地对部门程序源代码进行编译。这个过程被称为条件编译。
条件预处置惩罚器的布局与 if 选择布局很像。请看下面这段预处置惩罚器的代码:
  1. #ifdef NULL
  2.    #define NULL 0
  3. #endif
复制代码
可以只在调试时进行编译,调试开关可以使用一个宏来实现,如下所示:
  1. #ifdef DEBUG
  2.    cerr <<"Variable x = " << x << endl;
  3. #endif
复制代码
假如在指令 #ifdef DEBUG 之前已经界说了符号常量 DEBUG,则会对程序中的 cerr 语句进行编译。您可以使用 #if 0 语句解释掉程序的一部门,如下所示:
  1. #if 0
  2.    不进行编译的代码
  3. #endif
复制代码
实例:
  1. #include <iostream>
  2. using namespace std;
  3. #define DEBUG
  4. #define MIN(a,b) (((a)<(b)) ? a : b)
  5. int main ()
  6. {
  7.    int i, j;
  8.    i = 100
  9. ;
  10.    j = 30;
  11. #ifdef DEBUG
  12.    cerr <<"Trace: Inside main function" << endl;
  13. #endif
  14. #if 0
  15.    /* 这是注释部分 */
  16.    cout << MKSTR(HELLO C++
  17. ) << endl;
  18. #endif
  19.    cout <<"The minimum is " << MIN(i, j) << endl;
  20. #ifdef DEBUG
  21.    cerr <<"Trace: Coming out of main function" << endl;
  22. #endif
  23.     return 0;
  24. }
复制代码
输出:
  1. Trace: Inside main function
  2. The minimum is 30
  3. Trace: Coming out of main function
复制代码
3.4 # 和 ## 运算符

# 和 ## 预处置惩罚运算符在 C++ 和 ANSI/ISO C 中都是可用的。# 运算符会把 replacement-text 令牌转换为用引号引起来的字符串。
请看下面的宏界说:
  1. #include <iostream>
  2. using namespace std;
  3. #define MKSTR( x ) #x
  4. int main ()
  5. {
  6.     cout << MKSTR(HELLO C++
  7. ) << endl;
  8.     return 0;
  9. }
复制代码
输出:
  1. HELLO C++
复制代码
让我们来看看它是如何工作的。不难明白,C++ 预处置惩罚器把下面这行:
  1. cout << MKSTR(HELLO C++
  2. ) << endl;
复制代码
转换成了:
  1. cout << "HELLO C++
  2. " << endl;
复制代码
## 运算符用于毗连两个令牌。下面是一个实例:
  1. #define CONCAT( x, y )  x ## y
复制代码
当 CONCAT 出如今程序中时,它的参数会被毗连起来,并用来取代宏。例如,程序中 CONCAT(HELLO, C++) 会被替换为 "HELLO C++
",如下面实例所示。
  1. #include <iostream>
  2. using namespace std;
  3. #define concat(a, b) a ## b
  4. int main()
  5. {
  6.    int xy = 100
  7. ;
  8.    
  9.    cout << concat(x, y);
  10.    return 0;
  11. }
复制代码
输出:
  1. 100
复制代码
让我们来看看它是如何工作的。不难明白,C++ 预处置惩罚器把下面这行:
  1. cout << concat(x, y);
复制代码
转换成了:
  1. cout << xy;
复制代码
3.5 C++中的预界说宏

宏描述__LINE__这会在程序编译时包罗当前行号。__FILE__这会在程序编译时包罗当前文件名。__DATE__这会包罗一个形式为 month/day/year 的字符串,它表示把源文件转换为目的代码的日期。__TIME__这会包罗一个形式为 hour:minute:second 的字符串,它表示程序被编译的时间。 实例:
  1. #include <iostream>
  2. using namespace std;
  3. int main ()
  4. {
  5.     cout << "Value of __LINE__ : " << __LINE__ << endl;
  6.     cout << "Value of __FILE__ : " << __FILE__ << endl;
  7.     cout << "Value of __DATE__ : " << __DATE__ << endl;
  8.     cout << "Value of __TIME__ : " << __TIME__ << endl;
  9.     return 0;
  10. }
复制代码
输出:
  1. Value of __LINE__ : 6
  2. Value of __FILE__ : test.cpp
  3. Value of __DATE__ : Feb 28 2011
  4. Value of __TIME__ : 18:52:48
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

九天猎人

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表