小朋友学C++(21):命名空间

2018-07-25 12:28:41 浏览数 (1)

(一)

先看一个简单的程序:

代码语言:javascript复制
#include <iostream>
using namespace std;
int main()
{
    int a = 1;
    cout << a << endl;
}

运行结果:

代码语言:javascript复制
1

这里的第一行,#include好理解,iostream是输入输出流,包含了输入流istream和输出流ostream。 第二行using namespace std; 这里的namespace是个关键字,表示它后面的std是一个命名空间。

什么是命名空间呢? 假定三年1班有一名小学生叫做寒上耕,三年2班也有一名小学生叫做寒上耕,四年3班也有一名小学生叫寒上耕,假如有个人说:我要找寒上耕。那么没人知道他要找哪位寒上耕。但是假如有个人说:我要找三年1班的寒上耕,大家就知道他要找的是认谁。 这里的三年1班,三年2班,四年3班,就是命名空间,作用是防止命名冲突。

那么程序里为何要使用命名空间std呢?不用行不行? 若把using namespace std;这行代码去掉,cout和endl会报错,程序不认识这两个词。

(二)

咱们看一个iostream中的代码(我用的编译器是CodeBlocks):

代码语言:javascript复制
#ifndef _GLIBCXX_IOSTREAM
#define _GLIBCXX_IOSTREAM 1
#pragma GCC system_header
#include <bits/c  config.h>
#include <ostream>
#include <istream>
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
  /**
   *  @name Standard Stream Objects
   *
   *  The &lt;iostream&gt; header declares the eight <em>standard stream
   *  objects</em>.  For other declarations, see
   *  http://gcc.gnu.org/onlinedocs/libstdc  /manual/io.html
   *  and the @link iosfwd I/O forward declarations @endlink
   *
   *  They are required by default to cooperate with the global C
   *  library's @c FILE streams, and to be available during program
   *  startup and termination. For more information, see the section of the
   *  manual linked to above.
  */
  //@{
  extern istream cin;        /// Linked to standard input
  extern ostream cout;        /// Linked to standard output
  extern ostream cerr;        /// Linked to standard error (unbuffered)
  extern ostream clog;        /// Linked to standard error (buffered)
#ifdef _GLIBCXX_USE_WCHAR_T
  extern wistream wcin;        /// Linked to standard input
  extern wostream wcout;    /// Linked to standard output
  extern wostream wcerr;    /// Linked to standard error (unbuffered)
  extern wostream wclog;    /// Linked to standard error (buffered)
#endif
  //@}
  // For construction of filebuffers for cout, cin, cerr, clog et. al.
  static ios_base::Init __ioinit;
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace
#endif /* _GLIBCXX_IOSTREAM */

咱们看到了在iosteam中,cin(输入),cout(输出),cerr(错误),clog(日志)都是在std里定义的。

若你用的是Mac系统的Xcode编译器,则iostream头文件中的内容如下所示:

代码语言:javascript复制
C   -*-
//===--------------------------- iostream ---------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef _LIBCPP_IOSTREAM
#define _LIBCPP_IOSTREAM
/*
    iostream synopsis
#include <ios>
#include <streambuf>
#include <istream>
#include <ostream>
namespace std {
extern istream cin;
extern ostream cout;
extern ostream cerr;
extern ostream clog;
extern wistream wcin;
extern wostream wcout;
extern wostream wcerr;
extern wostream wclog;
}  // std
*/
#include <__config>
#include <ios>
#include <streambuf>
#include <istream>
#include <ostream>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#pragma GCC system_header
#endif
_LIBCPP_BEGIN_NAMESPACE_STD
#ifndef _LIBCPP_HAS_NO_STDIN
extern _LIBCPP_FUNC_VIS istream cin;
extern _LIBCPP_FUNC_VIS wistream wcin;
#endif
#ifndef _LIBCPP_HAS_NO_STDOUT
extern _LIBCPP_FUNC_VIS ostream cout;
extern _LIBCPP_FUNC_VIS wostream wcout;
#endif
extern _LIBCPP_FUNC_VIS ostream cerr;
extern _LIBCPP_FUNC_VIS wostream wcerr;
extern _LIBCPP_FUNC_VIS ostream clog;
extern _LIBCPP_FUNC_VIS wostream wclog;
_LIBCPP_END_NAMESPACE_STD
#endif  // _LIBCPP_IOSTREAM

可以看到,CodeBlocks和Xcode关于iostream的内容,有不小的差异。但是一些关于C 的标准,还是一致的。

(三)

那么endl是在哪里定义的呢? 咱们看一下定义endl的源码,具体是定义在ostream里面

代码语言:javascript复制
#ifndef _GLIBCXX_OSTREAM
#define _GLIBCXX_OSTREAM 1
#pragma GCC system_header
#include <ios>
#include <bits/ostream_insert.h>
namespace std _GLIBCXX_VISIBILITY(default)
{
  ......
  ......
_GLIBCXX_BEGIN_NAMESPACE_VERSION
  template<typename _CharT, typename _Traits>
    inline basic_ostream<_CharT, _Traits>&
    endl(basic_ostream<_CharT, _Traits>& __os)
    { return flush(__os.put(__os.widen('n'))); }
  ......
  ......
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
#include <bits/ostream.tcc>
#endif    /* _GLIBCXX_OSTREAM */

可以看到,endl是定义在ostream中的std中。 因为iostream头文件已经包含了ostream头文件和istream头文件,所以咱们的代码中只需要包含iostream头文件即可。

另一方面,咱们注意到,在iostream头文件和ostream头文件中都包含了命名空间std。可见命名空间可以散布到不同的头文件。事实上,std不止在这两个头文件中有,在其他的头文件中也有,作用是把一些常用的操作符都包含进来。然后在你写的代码中引入using namespace std;后,即可减少命名冲突。

(四)

假如咱们不写using namepace std;那么在使用相关的操作符时,需要加上std

代码语言:javascript复制
#include <iostream>
int main()
{
    int a = 1;
    std::cout << a << std::endl;
}

再看一个例子

代码语言:javascript复制
#include <iostream>
namespace A
{
    int x = 10;
};
namespace B
{
    int x = 20;
};
using namespace B;
int main()
{
    std::cout << x << std::endl;
}

这个程序里,有两个命名空间A和B,都定义了变量x。因为已经说明了using namespace B; 所以打印出20。

(五)

若没说明使用哪个命名空间,必须在x前面指明命名空间,否则编译会报错。

代码语言:javascript复制
#include <iostream>
namespace A
{
    int x = 10;
};
namespace B
{
    int x = 20;
};
int main()
{
    std::cout << B::x << std::endl;
    std::cout << A::x << std::endl;
}

运行结果:

代码语言:javascript复制
20
10

0 人点赞