learncpp-7 作用域、生存期、链接

7 作用域、生存期、链接

7.2 用户自定义命名空间和范围解析运算符

  • 为了避免命名冲突,在尽可能小的作用域内定义标识符
  • 一个命名空间要么在全局作用域内定义,要么在另一个命名空间内定义
  • 使用范围解析运算符::可以告诉编译器去指定命名空间查找指定标识符(如果::前没有命名空间则表示全局命名空间)
  • 如果使用标识符时没有带范围解析运算符,则编译器首先在使用该标识符的命名空间内查找是否有匹配的声明;如果没有则往外层的命名空间继续查找;最后查找全局命名空间
// 以下代码输出Foo; 如果注释掉Foo里的print()则输出Out;如果再注释掉Out里的print()则输出global
#include "iostream"
void print() {
    std::cout << "global";
}
namespace Out {
    void print() {
        std::cout << "Out";
    }
    namespace Foo {
        void print() {
            std::cout << "Foo";
        }
        void printHelloThere() {
            print();
        }
    }
}
int main() {
    Out::Foo::printHelloThere();
    return 0;
}
  • 对于一个命名空间里的标识符,它们的前向声明也应该放在该命名空间里
// add.h
namespace BasicMath{
  int add(int x,int y);// 在BasicMath命名空间里声明add()
}
// add.cpp
namespace BasicMath{
  int add(int x,int y){ // 在BasicMath命名空间里定义add()
    return x+y;
  }
}
// main.cpp
#include "add.h" // 为了引入BasicMath::add()
#include <iostream>
int main(){
  std::cout<<BasicMath::add(3,4)<<'\n';
  return 0;
}

如果add()前向声明没有放在BasicMath命名空间里,则add()会在全局作用域中声明,则编译器会报错找不到BasicMath::add()的声明;
如果add()的定义没有放在BasicMath命名空间里,编译不会报错,但是链接器会报错BasicMath::add()未定义

  • 可以在多个位置(跨多个文件或一个文件的多个位置)声明命名空间块

标准库大量使用了这个特性,因为每个标准库头文件都包含了std命名空间的声明,否则整个标准库都得在一个头文件中定义
这意味着可以将自己的代码添加到std命名空间中,但这可能会导致未定义的行为,因为std命名空间不允许用户扩展

  • 嵌套命名空间有两种形式:namespace a{namespace b{}}或者namespace a::b{}(自从c++17支持)
  • 命名空间别名:namespace c = a::b;

7.13 匿名命名空间和内联命名空间

  • 匿名命名空间中声明的所有内容都被视为父命名空间的一部分
#include <iostream>
namespace // unnamed namespace
{
    void doSomething() // can only be accessed in this file
    {
        std::cout << "v1\n";
    }
}
int main()
{
    doSomething(); // we can call doSomething() without a namespace prefix
    return 0;
}
  • 匿名命名空间中的所有标识符都被视为具有内部链接,这意味着匿名命名空间的内容只能被本文件所访问(对于函数来说,这相当于将匿名命名空间中的所有函数定义为static函数)
#include <iostream>
static void doSomething() // can only be accessed in this file
{
    std::cout << "v1\n";
}
int main()
{
    doSomething(); // we can call doSomething() without a namespace prefix
    return 0;
}

使用匿名命名空间确保某些内容只在本文件中生效,这比将所有声明单独标记为static更加方便
匿名空间也可以使得用户自定义的类型只在本文件中生效,这没有其他方法可以做到
内联命名空间通常用于版本内容。和匿名命名空间一样,内联命名空间中所有声明的内容都被视为父命名空间的一部分,但是不会影响链接

posted @ 2024-07-22 22:20  dengkang1122  阅读(4)  评论(0编辑  收藏  举报