C++的起源可以追溯到1979年,当时Bjarne Stroustrup(本贾尼·斯特劳斯特卢普,这个翻译的名字不同的地方可能有差异)在贝尔实验室从事计算机科学和软件工程的研究工作。⾯对项目中复杂的软件开发任务,特别是模拟和操作系统的开发⼯作,他感受到了现有语⾔(如C语⾔)在表达能力、可维护性和可扩展性方面的不⾜。
1983年,Bjarne Stroustrup在C语⾔的基础上添加了面向对象编程的特性,设计出了C++语言的雏形,此时的C++已经有了类、封装、继承等核心概念,为后来的面向对象编程奠定了基础。这⼀年该语⾔被正式命名为C++。
在随后的⼏年中,C++在学术界和⼯业界的应⽤逐渐增多。⼀些⼤学和研究所开始将C++作为教学和研究的⾸选语言,而⼀些公司也开始在产品开发中尝试使⽤C++。这⼀时期,C++的标准库和模板等特性也得到了进⼀步的完善和发展。
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++标准化的进程。
1997年11⽉14⽇,联合标准化委员会通过了该标准的最终草案。1998年,C++的ANSI/IS0标准被投入使用。
C++兼容C语言绝⼤多数的语法,所以C语言实现的hello world依旧可以运行,C++中需要把定义文件代码后缀改为.cpp,vs编译器看到是.cpp就会调用C++编译器编译,linux下要用g++编译,不再是gcc。
在C语言的学习中,我们写过C语言的第一个程序:
#include<stdio.h>
int main()
{
printf("hello world!");
return 0;
}当然C++有⼀套自己的输入输出,严格说C++版本的hello world应该是这样写的。
#include<iostream>
using namespace std;
int main()
{
cout << "hello world!" << endl;
return 0;
}在上面的代码中,有很多新的东西,比如:iostream,namespace,std,cout,endl......这些究竟是什么?在接下来的学习中,我们都会学习到。
在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。
c语言项目类似下面程序这样的命名冲突是普遍存在的问题,C++引入namespace就是为了更好的解决这样的问题
#include<stdio.h>
#include<stdlib.h>
int rand = 10;
int main()
{
printf("%d\n", rand);
return 0;
}当我们运行上面代码的时候,会出现下面的错误提示:

原因:在stdlib.h这个头文件中,rand是一个函数名,通过前面的学习,函数是作用于全局域的,而我们自己定义的变量rand也是一个全局变量,这就会出现名字冲突,为了解决这个问题,Bjarne博士发明了命名空间(namespace)了。
1、定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接⼀对{}即可,{}中即为命名空间的成员。命名空间中可以定义变量/函数/类型等。
namespace st//st为命名空间的名字
{
//命名空间中定义变量/函数/类型
int a = 10;
struct node
{
int* arr;
int size;
int capacity;
};
int Add(int left, int right)
{
return left + right;
}
}命名空间域的生命周期是整个函数,函数开始命名空间域开始,函数结束命名空间域结束,我们可以认为在命名空间中的是全局变量,提醒:不同域中可以定义同名的东西,但是相同域中不可以定义同名的东西,会发生冲突。
提醒:namespace 域名 { } 其中 { } 的括号后面没有“;”,这个要和定义结构体要有区别
2、namespace本质是定义出⼀个域,这个域跟全局域各自独立,不同的域可以定义同名变量,所以下面的rand不在冲突了。
代码示例:
#include<stdio.h>
#include<stdlib.h>
namespace st
{
int rand = 10;
struct node
{
int* arr;
int size;
int capacity;
};
int Add(int left, int right)
{
return left + right;
}
}
int rand = 10;
int main()
{
//这里默认是访问全局变量的函数指针rand
printf("%p\n", rand);
//这里访问的是namespace命名空间中的rand
printf("%d\n", st::rand);
return 0;
}3、C++中域有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找⼀个变量/函数/类型出处(声明或定义)的逻辑,所以有了域隔离,名字冲突就解决了。局部域和全局域除了会影响编译查找逻辑,还会影响变量的生命周期,命名空间域和类域不影响变量生命周期。
4、namespace只能定义在全局,当然他还可以嵌套定义。
namespace st
{
namespace st1
{
int rand = 10;
struct node
{
int* arr;
int size;
int capacity;
};
int Add(int left, int right)
{
return left + right;
}
}
namespace st2
{
int rand = 10;
struct node
{
int* arr;
int size;
int capacity;
};
int Add(int left, int right)
{
return left + right;
}
}
}写到这里,就会有小伙伴提出疑问了?那既然有了命名空间。那我们该如何使用这个命名空间呢?
在学习如何使用这个命名空间之前,我们先学一个东西:
域操作符
:: 域操作符 如果::的左边没有任何东西,表示全局,如果有命名空间的名字,则表示该域中的东西
例如:

通过学习域操作符,接下来,让我们看看如何使用命名空间:
namespace st
{
int rand = 10;
struct node
{
int* arr;
int size;
int capacity;
};
int Add(int left, int right)
{
return left + right;
}
}
int b = 20;
int main()
{
int a = 10;
// 编译器语法查找确认,默认规则先局部查找->全局查找->没有找到就报错未声明的标识符
//默认是不会去命名空间的域中查找
printf("%d\n", a);
//::域操作符
//如果::的左边没有任何东西,表示全局,如果有东西,则表示该域中的东西
printf("%d\n", ::b);
printf("%d\n", st::rand);
printf("%d\n", st::Add(1, 2));
//使用命名空间中的结构体需要像下面的写法
struct st::node node1;
return 0;
}
//嵌套命名空间域的使用
namespace st
{
namespace st1
{
int rand = 10;
struct node
{
int* arr;
int size;
int capacity;
};
int Add(int left, int right)
{
return left + right;
}
}
namespace st2
{
int rand = 10;
struct node
{
int* arr;
int size;
int capacity;
};
int Add(int left, int right)
{
return left + right;
}
}
}
int main()
{
//调用st1中的加法函数
st::st1::Add(2, 3);
//调用st2中的加法函数
st::st2::Add(3, 4);
return 0;
}注意:编译器语法查找确认,默认规则先局部查找->全局查找->没有找到就报错未声明的标识符,默认是不会去命名空间域中查找,除非使用域操作符,使用了域操作符,则直接去该域中查找->没有找到就报错未声明的标识符
5、项目工程中多文件中定义的同名namespace会认为是⼀个namespace,不会冲突。



这两个命名空间不会冲突,只会合并 6、C++标准库都放在⼀个叫std(standard)的命名空间中。
每当使用C++库时需要加上std
cin是把这个字符串(因为我们从键盘上输入的都可以认为是字符串)转换成int,char等类型

int i=10;std::cout<<i; 把i这个类型的对象转换成字符串插入到cout中,然后输出; std::cout<<i 的特点是自动识别类型,并且不同类型可以连续在一起; 任何变量都转换成字符串插入到流中(可以认为是控制台)

//换行
int main()
{
int i = 0;
std::cout << i << '\n';//-->不刷新缓冲区
std::cout << i << "\n";
std::cout << i << std::endl;//-->刷新缓冲区
return 0;
}建议在竞赛中使用上面两个换行,上面两个不刷新缓冲区

写到这里不知道会不会有小伙伴会有疑问?上面我们在写换行的代码时,每写一句都要写上std::cout,std::endl,以及std::cin,每当我使用cout和cin的时候都要加上std,感觉好麻烦,那这个std可不可以不写?接下来我们来看一下
编译查找⼀个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找。所以下⾯程序会编译报错。
namespace st
{
int rand = 10;
struct node
{
int* arr;
int size;
int capacity;
};
int Add(int left, int right)
{
return left + right;
}
}
int main()
{
printf("%d\n", rand);
return 0;
}当我们运行上面代码时,编译器会报错,如果不使用域操作符,编译器默认先从局部域中查找是否存在变量rand,如果没有,则在全局域中查找变量rand,如果都没有,则报错。
namespace st
{
int rand = 10;
struct node
{
int* arr;
int size;
int capacity;
};
int Add(int left, int right)
{
return left + right;
}
}
int main()
{
printf("%d\n", st::rand);
return 0;
}所以我们要使用命名空间中定义的变量/函数,有三种方式:
#include<iostream>
using std::cout;//-->部分展开命名空间
using std::endl;
int main()
{
int i = 10;
cout << i << endl;
return 0;
}
using namespace std;//-->展开命名空间(全部展开)
int main()
{
int i = 10;
cout << i << endl;
return 0;
}
总结:
