标C++
背景介绍
C++完美兼容C
用c++编译器编译c代码发现完美通过
#include <stdio.h>
#include <iostream>
int main(void){
std::cout << "hello world" << std::endl;
return 0;
}
第一个C++程序
告诉编译器要使用gcc里面的g++来编译这个代码
day01$gcc 01_hello.cpp -o 01_hello -lstdc++
后缀名可以使用c/cpp/cxx/cc,一般使用cpp:
day01$mv 01_hello.cpp 01_hello.cxx
day01$ls
01_hello 01_hello.cxx
day01$01_hello
hello world
初步认识iostream
iostream路径:
观察一下以下三个:
c++库的cstdio包括了c库的stdio.h:
cstdlib和ctring亦然。
名字空间
代码示例:
day01$vi nameconf.cpp
//用来维护工行卡的余额
#include <iostream>
int g_money = 0;
//存
void save(int money){
g_money += money;
}
//花
void pay(int money){
g_money -= money;
}
int main(void){
save(10000);
pay(3000);
std::cout << "工行卡余额:" << g_money << std::endl;
return 0;
}
day01$g++ nameconf.cpp -o nameconf
day01$ls
01_hello 01_hello.cpp nameconf nameconf.cpp
day01$nameconf
工行卡余额:7000
现在假设还要维护一张建行卡,把第3行~第9行的代码复制到第9行的下面:
回车后结果如下:
此时会报重定义的错误,不改名字该如何解决呢?
———通过名字空间。namespace就是在全局域和局部域之间又建立了一层作用域
1.名字空间定义
//用来维护工行卡的余额
#include <iostream>
namespace ICBC{
int g_money = 0;
//存
void save(int money){
g_money += money;
}
//花
void pay(int money){
g_money -= money;}
}
namespace CCB{
int g_money = 0;
//存
void save(int money){
g_money += money;
}
//花
void pay(int money){
g_money -= money;
}
}
int main(void){
ICBC::save(10000);
ICBC::pay(3000);
std::cout << "工行卡余额:" << ICBC::g_money << std::endl;
CCB::save(10000);
CCB::pay(7000);
std::cout << "建行卡余额:" << CCB::g_money << std::endl;
return 0;
}
day01$g++ nameconf.cpp -o nameconf
day01$./nameconf
工行卡余额:7000
建行卡余额:3000
save和pay现在没有直接暴露在全局域中,不能直接用;但是ICBC和CCB暴露在全局域中,可以直接用。
2.名字空间合并
名字空间是可以拆开来写的(程序员拆开,编译器合并):
3.声明定义分开
以CCB
为例:
namespace CCB{
int g_money = 0;
//存
void save(int money){// 连 声明 带 定义
g_money += money;
}
//花
void pay(int money);// 声明
}
void pay(int money){// 定义
g_money -= money;
}
如果要把声明和定义分开来写的话,一定要让它们保持契合/一致性;
上述CCB
代码中声明的pay
是名字空间当中的,而第10行的pay
也可能是全局变量,它不能体现出是第8行声明过的pay
的定义。
可以这么改:
void CCB::pay(int money){// 定义
g_money -= money;
}
使用名字空间的三种方式:
1.作用域限定符(高可靠):::
2.名字空间指令(低可靠):
day01$cp 02_nameconf.cpp 03_diruse.cpp
day01$vi 03_diruse.cpp
//名字空间指令
#include <iostream>
namespace ns{
int g_value;
}
int main(void){
using namespace ns;// 从这行代码开始 ns中的所有内容在 当前作用域 可见
g_value = 100;
std::cout << "ns::g_value = " << ns::g_value << std::endl;
return 0;
}
day01$g++ 03_diruse.cpp -o 03_diruse
day01$./03_diruse
ns::g_value = 100
验证名字空间指令的低可靠:
1.定义一个局部变量g_value
//名字空间指令
#include <iostream>
namespace ns{
int g_value = 0;
}
int main(void){
int g_value = 0;
using namespace ns;// 从这行代码开始 ns中的所有内容在 当前作用域 可见
g_value = 100;// 5 / 9
std::cout << "ns::g_value = " << ns::g_value << std::endl;
return 0;
}
第10行访问的
g_value
是第4行的还是第9行的?———第9行
day01$./03_diruse
ns::g_value = 100
编译器在编译每一个函数之前,都会弄两张表,编译器用完了就会销毁,不存到代码里面,编译器拿到标识符先去定义表里找再去可见表里找,若在定义表当中找到了就不找了
2.把局部变量g_value
改成全局变量
//名字空间指令
#include <iostream>
namespace ns{
int g_value = 0;
}
int g_value = 0;
int main(void){
using namespace ns;// 从这行代码开始 ns中的所有内容在 当前作用域 可见
g_value = 100;// 5 / 7
std::cout << "ns::g_value = " << ns::g_value << std::endl;
return 0;
}
则第10行访问的
g_value
是第5行的还是第7行的?———编译器会报错
全局变量不是main
函数自己定义的,所以在可见表当中。
3.把using namespace ns;
也放到全局域当中同样报错,当前作用域是全局域,编译器不知道访问谁
//名字空间指令
#include <iostream>
namespace ns{
int g_value = 0;
}
int g_value = 0;
using namespace ns;// 从这行代码开始 ns中的所有内容在 当前作用域 可见
int main(void){
g_value = 100;// 5 / 7 / err
std::cout << "ns::g_value = " << ns::g_value << std::endl;
return 0;
}
最终代码:
//名字空间指令
#include <iostream>
using namespace std;
namespace ns{
int g_value = 0;
}
int main(void){
using namespace ns;// 从这行代码开始 ns中的所有内容在 当前作用域 可见
g_value = 100;
cout << "ns::g_value = " << ns::g_value << endl;
return 0;
}
3.名字空间声明(04_impname.cpp)
验证优点:
//名字空间申明
#include <iostream>
using namespace std;
namespace ns{
int g_value = 0;
}
int main(void){
using ns::g_value;// 从这行代码开始,ns中的g_value引入当前作用域(相当于定义)
g_value = 100;
cout << "ns::g_value = " << ns::g_value << endl;
return 0;
}
ns:g_value = 100
验证缺点(低可靠):
//名字空间申明
#include <iostream>
using namespace std;
namespace ns{
int g_value = 0;
}
int main(void){
int g_value = 0;
using ns::g_value;// 从这行代码开始,ns中的g_value引入当前作用域(相当于定义)
g_value = 100;// 5 / 9 / err
cout << "ns::g_value = " << ns::g_value << endl;
return 0;
}
结果会报重定义的错误:第9行是在定义,第10行又相当于定义。
练习:
名字空间指令和名字空间声明(dlchide.cpp),体验两者的差别:
vi 05_dlchide.cpp
//名字空间声明 和 名字看见指令的差别
#include <iostream>
using namespace std;
namespace ns1{
int g_value = 0;
int g_other = 0;
}
namespace ns2{
int g_value = 0;
int g_other = 0;
}
int main(void){
using namespace ns1;//名字空间指令,ns1中的内容出现在可见表中,所有内容都出现在可见表
using ns2::g_value;//名字空间声明,ns2中的g_value出现在定义表,只有g_value出现在定义表
g_value = 888;//ns2的
cout << "ns1::g_value = " << ns1::g_value << ",ns2::g_value = " << ns2::g_value << endl;
g_other = 666;//ns1的
cout << "ns1::g_other = " << ns1::g_other << ",ns2::g_other = " << ns2::g_other << endl;
return 0;
}
day01$./05_dlchide
ns1::g_value = 0,ns2::g_value = 888
ns1::g_other = 666,ns2::g_other = 0
解析:
1.g_value 通过 using ns2::g_value; 声明被直接引入当前作用域的定义表(也称为注入点),因此它在当前作用域中可直接访问,无需前缀
2.g_other 没有被显式声明,但由于 using namespace ns1; 指令将 ns1 的整个命名空间内容 引入了当前作用域的可见表(也称为候选项集合),因此 g_other 会被解析为 ns1::g_other。
名字空间嵌套
vi 06_netstalias.cpp
//名字空间嵌套
#include <iostream>
using namespace std;
namespace ns1{
int g_value = 100;
namespace ns2{
int g_value = 200;
namespace ns3{
int g_value = 300;
namespace ns4{
int g_value = 400;
}
}
}
}
int main(void){
namespace ns_four = ns1::ns2::ns3::ns4;//名字空间别名,不是给变量起别名
//cout << ns1::ns2::ns3::ns4::g_value << endl;
cout << ns_four::g_value << endl;//可以简化书写
return 0;
}
ps: 命名空间 == 名字空间 –>namespace