C++ 入门:发展史、命名空间与输入输出详解
C++ 语言起源于 1979 年,由 Bjarne Stroustrup 在贝尔实验室开发,旨在弥补 C 语言的不足。本文涵盖 C++发展历史、版本更新及参考文档资源。重点讲解命名空间(namespace)的作用、定义方式、嵌套使用及多文件合并机制,解决标识符冲突问题。同时介绍 C++标准输入输出流(iostream),对比 C 语言 printf/scanf,演示 cin/cout 用法及效率优化技巧。适合有 C 语言基础的初学者入门。

C++ 语言起源于 1979 年,由 Bjarne Stroustrup 在贝尔实验室开发,旨在弥补 C 语言的不足。本文涵盖 C++发展历史、版本更新及参考文档资源。重点讲解命名空间(namespace)的作用、定义方式、嵌套使用及多文件合并机制,解决标识符冲突问题。同时介绍 C++标准输入输出流(iostream),对比 C 语言 printf/scanf,演示 cin/cout 用法及效率优化技巧。适合有 C 语言基础的初学者入门。

1. 起源(1979-1983): C++的起源可以追溯到 1979 年,当时 Bjarne Stroustrup(本贾尼·斯特劳斯特卢普) 在贝尔实验室从事计算机科学和软件工程的研究工作。面对项目中复杂的软件开发任务,特别是模拟和操作系统的开发工作,他感受到了现有语言(如 C 语言)在表达能力、可维护性和可扩展性方面的不足。1983 年,Bjarne Stroustrup 在 C 语言的基础上添加了面向对象编程的特性,设计出了 C++语言的雏形,此时的 C++已经有了类、封装、继承等核心概念,为后来的面向对象编程奠定了基础。这一年该语言被正式命名为 C++。在随后的几年中,C++在学术界和工业界的应用逐渐增多。一些大学和研究所开始将 C++作为教学和研究的首选语言,而一些公司也开始在产品开发中尝试使用 C++。这一时期,C++的标准库和模板等特性也得到了进一步的完善和发展。
2. 标准化(1989-1994): C++的标准化工作于 1989 年开始,并成立了一个 ANSI 和 ISO(International Standards Organization)国际标准化组织的联合标准化委员会。1994 年标准化委员会提出了第一个标准化草案。在该草案中,委员会在保持斯特劳斯特卢普最初定义的所有特征的同时,还增加了部分新特征。在完成 C++标准化的第一个草案后不久,STL(Standard Template Library)是惠普实验室开发的一系列软件的统称。它是由 Alexander Stepanov、Meng Lee 和 David R Musser 在惠普实验室工作时所开发出来的。在通过了标准化第一个草案之后,联合标准化委员会投票并通过了将 STL 包含到 C++标准中的提议。STL 对 C++的扩展超出 C++的最初定义范围。虽然在标准中增加 STL 是个很重要的决定,但也因此延缓了 C++标准化的进程。
3. 正式投入使用(1997): 1997 年 11 月 14 日,联合标准化委员会通过了该标准的最终草案。1998 年,C++的 ANSI/ISO 标准被投入使用。
C++的诞生是为了弥补 C 语言的不足之处!所以部分的 C 语言依然能够在 c++程序中成功运行~
C++的版本更新基本上在 5 年左右更新一次,大家可以参考官方文档了解 C++的历代版本与更新内容。
说明:这两个文档各有优势,建议结合着使用。
C++ 的应用领域包括服务器端、游戏(引擎)、机器学习引擎、音视频处理、嵌入式软件、电信设备、金融应用、基础库、操作系统、编译器、基础架构、基础工具、硬件交互等很多方面。
C++兼容 C 语言绝大多数的语法,所以 C 语言实现的 hello world 依旧可以运行,C++中需要把定义文件代码后缀改为.cpp,vs 编译器看到是.cpp 就会调用 C++编译器编译,linux 下要用 g++编译,不再是 gcc。
// 这里使用的是 test.cpp
#include<stdio.h>
int main() {
printf("Hello World\n");
return 0;
}
然而 C++有自己的一套输入输出设备,所以在 C++中的 hello world 应该是这样:
#include<iostream>
using namespace std;
int main() {
cout << "hello world\n" << endl;
return 0;
}
在 C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace 关键字的出现就是针对这种问题的。
c 语言项目类似下面程序这样的命名冲突是普遍存在的问题,C++引入 namespace 就是为了更好的解决这样的问题。
// 命名冲突
#include<stdio.h>
#include<stdlib.h>
int rand = 10;
int main() {
// 编译报错:error C2365: 'rand': 重定义;以前的定义是'函数'
printf("%d", rand);
return 0;
}
因为库函数 stdlib.h 中已经定义了 rand,所以 C 语言就无法运行这串代码了。但在 C++的输入输出中就可以用 namespace 来解决这个问题。
#include<stdio.h>
#include<stdlib.h>
namespace A //A 是命名空间的名字,一般开发中是用项目名字做命名空间名
{
// 命名空间中可以定义变量/函数/类型
int rand = 10;
int ADD(int a,int b) {
return a + b;
}
struct Node {
struct Node* next;
int val;
};
}//注意这里没有分号!!
int a = 1;
int main() {
//编译器语法查找确认规则是默认先局部查找->全局查找->没有找到就报未声明的标识符这个错误
//::域作用限定符,这里指定作用域,就直接按这个域去找->没有找到就报未声明的标识符这个错误
int a = 0;
printf("%d\n", a);
printf("%d\n", ::a);//::前面不加域就是默认全局域!不会到命名空间里查找!
printf("%p\n", rand);//这里的 rand 是 stdlib.h 中的指针,要用%p 访问
printf("%d\n", A::rand);//这里访问的就是 A 域中的 rand
printf("%p\n", A::ADD);
printf("%d\n", A::ADD(1, 2));//这里是直接调用该函数
//A::struct Node node;//这是错误的
struct A::Node node;
return 0;
}
!!!这里需要注意的是命名空间域不会影响变量的生命周期(是一个全局的生命周期)。
#include<stdio.h>
#include<stdlib.h>
//命名空间的嵌套使用
namespace A {
namespace a {
int rand = 10;
int ADD(int a,int b) {
return a + b;
}
}
namespace b {
int rand = 20;
int ADD(int a, int b) {
return (a + b) * 10;
}
}
}
int main() {
printf("%d\n", A::a::rand);
printf("%d\n", A::b::rand);
printf("%d\n", A::a::ADD(1, 2));
printf("%d\n", A::b::ADD(1, 2));
return 0;
}
命名空间的嵌套可以嵌套很多层,但是不推荐,因为那样写起来也很不方便~
我们在项目中经常会用到命名空间的嵌套。
多文件中可以定义同名 namespace,他们会默认合并在一起,就像同一个 namespace 一样。下面给大家简单演示一下~
test.cpp
#include<stdio.h>
#include<stdlib.h>
#include"Stack.h"
#include"Queue.h"
int main() {
st::stack s1;
st::Init(&s1);
st::Push(&s1, 1);
st::Push(&s1, 2);
st::Pop(&s1);
queue::Queue q1;
queue::Init(&q1);
queue::Push(&q1, 1);
queue::Push(&q1, 2);
queue::Pop(&q1);
return 0;
}
stack.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
namespace st {
typedef int STDataType;
typedef struct stack {
STDataType* arr;
int top;
int capacity;
}stack;
void Init(stack* ps);
void Push(stack* ps, STDataType x);
void Pop(stack* ps);
}
stack.cpp
#include"Stack.h"
namespace st {
void Init(stack* ps) {
//......
}
void Push(stack* ps, STDataType x) {
//...
}
void Pop(stack* ps) {
//...
}
}
queue.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
namespace queue {
typedef int QDataType;
typedef struct QueueNode {
QDataType data;
struct QueueNode* next;
}QueueNode;
typedef struct Queue {
QueueNode* phead;
QueueNode* ptail;
}Queue;
void Init(Queue* pq);
void Push(Queue* pq, QDataType x);
void Pop(Queue* pq);
}
queue.cpp
#include"Queue.h"
namespace queue {
void Init(Queue* pq) {
//......
}
void Push(Queue* pq, QDataType x) {
//...
}
void Pop(Queue* pq) {
//...
}
}
编译查找一个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找。所以下面程序会编译报错。所以我们要使用命名空间中定义的变量/函数,有三种方式:
#include<iostream>//C++中不需要带.h
#include<algorithm>
int main() {
int a[5] = { 4,1,2,5 };
// 指定命名空间访问
std::sort(a, a + 4);
return 0;
}
#include<iostream>
#include<algorithm>
//命名空间中成员部分展开
using std::cout;
using std::cin;
int main() {
int a, b;
cin >> a >> b;//输入
cout << a << " " << b << '\n';//输出
return 0;
}
在这里我们可以看到部分展开了之后 cout 就不需要在前面加东西了,但是 cin 前面还是需要加 std 的。这个展开方式还是推荐在大型项目中使用的~
#include<iostream>
#include<algorithm>
//命名空间中成员全部展开
using namespace std;
int main() {
int a, b;
cin >> a >> b;//输入
cout << a << " " << b << '\n';//输出
return 0;
}
这里就是把 std 中的所有成员全部展开,在做算法题或者小程序没有命名冲突的情况下可以使用,而且使用起来比较方便,但是在大型项目中就不推荐使用了~
举例说明:
#include<iostream>
int main() {
int i = 100;
double b = 1.11000;
//任何变量都会转换成字符串,插入到流中
//cout 会自动识别
std::cout << i << "\n";
std::cout << b << "\n";
std::cout << "hello world" << std::endl;//end line
std::cout << "hello" << " " << "world" << std::endl;
std::cin >> i >> b;//cin 是输入
std::cout << i << " " << b << std::endl;//cout 是输出
return 0;
}
换行用 '\n' 的效率会比使用 endl 更高,除此以外,cout 和 cin 的效率其实也是不如 printf 和 scanf 的,但是 printf 和 scanf 不能处理类(也就是更复杂的类型),而 C++的 IO 流支持复杂类型的输入输出。在 C++的 IO 流中我们可以通过取消同步流的操作来解决这个问题,代码如下:
#include<iostream>
using namespace std;
int main() {
// 在 io 需求比较高的地方,如部分大量输入的竞赛题中,加上以下 3 行代码
// 可以提高 C++IO 效率
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
return 0;
}

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online