C++ 自己实现cout和为什么使用 using namespace std;

简介: 前排提示:阅读本篇文章需要至少学过类和对象以及重载运算符的概念。

接触这么久 C++ 了,我想尝试写一个自己的 cout 了。在实现之前,先扯扯几个概念


一、cout 是谁?


不就是输出语句嘛?!这个答案太浅显。

正确答案: cout 是类 ostream 的一个对象,而这个对象有一个成员重载运算符函数operator <<。顺便一说,类 ostream 又属于 iostream 库中,iostream 是标准的 C++ 头文件。既然如此,那么这个头文件里应该是这样写的:

1.png

这个 ostream::operator << 有多个重载版本,所以形参什么类型都有,包括 int,double,char 等等。当我们写 cout<<a 时,其实也可以写成 cout.operator<<(a)


cout 的语句格式是:cout<<表达式1<<表达式2<<……<<表达式n; ,那么如果按照以上的写法,我们只能写诸如 cout<<a; 的语句,而不能写成 cout<<a<<b<<c; 这样连起来的形式,可是 C++ 里面却能实现这样的写法,这是为什么呢?


原来在 ostream::operator << 里还有一句 return *this 。当执行语句 cout<<a<<b<<c; 时,先执行 cout.operator<<(a) ,然后这个函数会返回 cout,其实就是返回自己本身,然后再去执行 cout.operator<<(b) ,然后又会去执行下一个函数,以此类推。


因此可以这样实现:

2.png


二、什么是namespace?


我们经常在程序开头写 using namespace std; 但有没有想过是干什么用的?


顾名思义,namespace 的意思是 命名空间,它是用来组织和重用代码的。那这个到底有什么用呢?


好,我们在脑中想这么一种情况:你写了一个库文件,里面有个名字叫 fun 的函数,很不幸你另外一个人写的库文件也有个名字叫 fun 的函数,这样就冲突了。


3.png


这时,我们就想了一个办法:

     把你写的 fun 函数放到一个你命名的空间里,把另外一个人写的 fun 函数放到另一个命名空间里。

     只要有人说要用你的空间,那么他用的 fun 函数肯定是你写的 fun 函数,不会是另外一个人的。

     如果那个人说要用另一个人的空间,那么他用的 fun 函数肯定是另一个人写的 fun 函数,不会是你的。

4.png

如果,你直接使用 cout 而不使用 namespace ,那系统不知道你在用的是谁的 cout (没准有多个 cout 呢?)

扯了这么多,其实这里面表达的意思是:为了解决名字冲突问题,引入了名字空间这个概念,通过使用 namespace xxx; 你所使用的库函数或变量就是在该名字空间中定义的,这样一来就不会引起不必要的冲突了。


那我们为什么要写 using namespace std; ?早期的 C 将标准库功能定义在全局空间里,声明在带 .h 后缀的头文件里。


C++ 标准为了和 C 区别开,也为了正确使用命名空间,规定头文件不使用后缀 .h 。因此,当使用 <iostream.h> 时,相当于在 C 中调用库函数,使用的是全局命名空间,这也是早期的 C++ 实现;


当使用 <iostream> 的时候,该头文件没有定义全局命名空间,必须使用 namespace std; 这样才能正确使用 cout。

假如不写 using namespace std;,那就要写成 std::cout<< 了。每一句都这么写很烦,于是干脆在程序开头直接来一句 using namespace std;

好了,其实这个 iostream 库大概是这么写的:

5.png


三、自己实现 cout


以上两个已经讲得很清楚了,下面上实现 cout 的代码:

6.png

本代码已经通过编译,并且能正确输出。对于C++的这些特性,当我们学得深入了以后,就很有必要弄懂了。








目录
相关文章
|
8月前
|
存储 前端开发 安全
C++一分钟之-未来与承诺:std::future与std::promise
【6月更文挑战第27天】`std::future`和`std::promise`是C++异步编程的关键工具,用于处理未完成任务的结果。`future`代表异步任务的结果容器,可阻塞等待或检查结果是否就绪;`promise`用于设置`future`的值,允许多线程间通信。常见问题包括异常安全、多重获取、线程同步和未检查状态。解决办法涉及智能指针管理、明确获取时机、确保线程安全以及检查未来状态。示例展示了使用`std::async`和`future`执行异步任务并获取结果。
196 2
|
2月前
|
存储 对象存储 C++
C++ 中 std::array<int, array_size> 与 std::vector<int> 的深入对比
本文深入对比了 C++ 标准库中的 `std::array` 和 `std::vector`,从内存管理、性能、功能特性、使用场景等方面详细分析了两者的差异。`std::array` 适合固定大小的数据和高性能需求,而 `std::vector` 则提供了动态调整大小的灵活性,适用于数据量不确定或需要频繁操作的场景。选择合适的容器可以提高代码的效率和可靠性。
101 0
|
5月前
|
安全 C++
C++: std::once_flag 和 std::call_once
`std::once_flag` 和 `std::call_once` 是 C++11 引入的同步原语,确保某个函数在多线程环境中仅执行一次。
|
5月前
|
C语言 C++
C++(六)Namespace 命名空间
命名空间(Namespace)是为了解决大型项目中命名冲突而引入的机制。在多库集成时,不同类库可能包含同名函数或变量,导致冲突。C++通过语法形式定义了全局无名命名空间,并允许对全局函数和变量进行作用域划分。命名空间支持嵌套与合并,便于协同开发。其使用需谨慎处理同名冲突。
|
7月前
|
存储 C++ 运维
开发与运维函数问题之使用C++标准库中的std::function来简化回调函数的使用如何解决
开发与运维函数问题之使用C++标准库中的std::function来简化回调函数的使用如何解决
69 6
|
7月前
|
C++ 运维
开发与运维编译问题之在C++中在使用std::mutex后能自动释放锁如何解决
开发与运维编译问题之在C++中在使用std::mutex后能自动释放锁如何解决
91 2
|
7月前
|
C++
C++一分钟之-类型别名与using声明
【7月更文挑战第20天】在C++中,类型别名和`using`声明提升代码清晰度与管理。类型别名简化复杂类型,如`using ComplexType = std::vector&lt;std::shared_ptr&lt;int&gt;&gt;;`,需注意命名清晰与适度使用。`using`声明引入命名空间成员,避免`using namespace std;`全局污染,宜局部与具体引入,如`using math::pi;`。恰当应用增强代码质量,规避常见陷阱。
110 5
|
8月前
|
安全 C++
C++一分钟之-字符串处理:std::string
【6月更文挑战第25天】`std::string`是C++文本处理的核心,存在于`&lt;string&gt;`库中。它支持初始化、访问、连接、查找、替换等操作。常见问题包括空指针解引用、越界访问和不当内存管理。要安全使用,确保字符串初始化,用`at()`检查边界,用`.empty()`检查空字符串,且无需手动释放内存。高效技巧包括预先分配内存、利用互转函数以及使用迭代器。记得正确比较和遍历字符串以保证代码效率和安全性。
111 5
|
8月前
|
存储 设计模式 安全
C++一分钟之-并发编程基础:线程与std::thread
【6月更文挑战第26天】C++11的`std::thread`简化了多线程编程,允许并发执行任务以提升效率。文中介绍了创建线程的基本方法,包括使用函数和lambda表达式,并强调了数据竞争、线程生命周期管理及异常安全等关键问题。通过示例展示了如何用互斥锁避免数据竞争,还提及了线程属性定制、线程局部存储和同步工具。理解并发编程的挑战与解决方案是提升程序性能的关键。
116 3
|
8月前
|
C++
C++一分钟之—名空间(namespace)的作用与使用
【6月更文挑战第22天】C++的命名空间是代码组织的关键,防止命名冲突并促进模块化。通过`namespace`定义代码块,如`MyNamespace{...}`,并使用别名(`namespace MN = MyNamespace::...`)简化引用。避免过度使用和嵌套,以及`using namespace`的滥用,而应在小范围内导入所需成员。明智选择名空间名称并有效利用`using`声明,能提升代码可读性和专业性。
254 1