我们知道C++是C的超集。它向下兼容C语法,同时又在C的基础之上扩展了一些功能。我们一般将C++分为以下三个方面:
- C++对C的扩展
- C++面向对象
- C++ STL
本系列博客主要关注于前两个部分C++对C的扩展以及C++面向对象部分。C++ STL有专门的系列讲解。
学习C++有两本书不可不提,一本是C++ primer另一本是C++ primer plus。前者是学习C++ 的终身参考书,后者是入门C++必看的材料。
那么我们就从C++对C的扩展开始讲起吧。
namespace
- 什么是namespace?
namespace是变量作用域的范围。我们可以把namespace看成是花括号的“别名”。 - namespace有什么用?
举个例子。一个办公楼里有两个公司,分别为A,B。A公司和B公司里都有一个张三。现在你在办公楼下面,想要找公司A里的张三,你该怎么做?如果你在楼下喊张三,这个时候A公司和B公司的张三都出来了,这产生了冲突,所以出现了错误。如果想要避免冲突就要加上限定,我要找的是A公司的张三。这个就是namespace的思想,为指针变量函数结构体或者类加上作用范围,这样能够避免命名冲突。 namespace的实现
namespace的定义
namespace namespaceA{ int a=0; int b=0; } //namespace namespace_name{xxx} namespace namespaceB{ int a=1; int b=1; }
- namespace的使用
- 单个
在变量或者函数前加上作用域。例如:std::cout<<"test"<<std::endl;
- 添加一类name
每一次输出都要写一次std::cout
太麻烦了,让cout
直接表示std::cout
就好了。
使用using std::cout;
即可添加命名空间中的某一种变量或函数等。 - 添加整个命名空间
这个就是我们常用的using namespace std;
了。
- 单个
需要注意的是namespace的嵌套定义
namespace A{ namespace B{ int a=0; } } //问题:using namespace A;可以直接操作变量a吗? //答案:不能,因为namespace A下可能有namespace C;C中也可能含有变量a,这是无法确定的。C++直接从语法上避免了这种冲突可能的存在。
- 什么是namespace?
普通变量的定义
C++中变量的定义可以在任意位置
而C语言中,变量必须定义在函数开始位置。全局变量的定义
C++中增强了对全局变量定义的检查,不能重复定义全局变量。
C中可以重复定义全局变量。结构体的定义
C++:struct Teacher{ int id; char name[64]; }; int main(){ Teacher t1; //Teacher就是一种数据类型 }
C:
struct Teacher{ int id; char name[64]; }; int main(){ Teacher t1;//错误 struct Teacher t1;//必须加上struct 表明数据类型为struct Teacher }
如果对上述内容仍然有疑问,直接在定义struct的时候使用typedef自定义数据类型即可。
函数参数与返回类型检测加强
C++://C++ 中的函数必须有返回类型,否则编译报错 //C++中的形参和实参个数必须对应,否则报错 g(){ }//错,没有返回类型 int g(void){ return 0; } g(1,2,3)//错,实参个数和形参个数不一致
//C语言说:我还从来没见过函数定义和调用还能出错的 //上述两个错误,在C语言中,都不是错误。这是因为C语言编译器设计的太早了,
bool数据类型
C++中新增的bool数据类型。
我们知道,一些比较新的语言都有bool数据类型。但是C语言没有。。。在C语言中,我们是通过变量的数值来判断逻辑真与逻辑假的。0为逻辑假,非0位逻辑真。- bool数据类型的取值
bool数据类型的变量只有两个值true和false - bool数据类型与int数据类型的互换
0为false,非0位true
false输出为0,true输出为1 - bool数据类型占内存空间
一字节
- bool数据类型的取值
三目运算符
C++中三目运算符可以做左值而C中三目运算符只能做右值。这是由C++中三目运算符的实现决定的。int a=10; int b=20; (a<b)?a:b=50; //三目运算符返回的是变量a的别名 语法糖 实际上是*((a<b)?&a:&b)=50; cout<<a<<endl; (a<b)?10:b=30; xxxxxx也是错误的10=30 且常值没有取地址符运算同时也不能取※
const
在C++中,const才是真正的常量。
C中的const只是将变量加上只读属性,通过指针间接操作竟然还能修改其值。C中的const是冒牌货。C++ const的实现
C++定义的const变量没有地址,只存储在符号表(只读文件)中,此时的a就是一个符号,不能够改变,值由符号表决定。
这个时候就有个问题了,既然const变量没有存储在内存中,为什么可以对其进行取地址操作?
这是因为C++为const变量生成了一个匿名变量,我们对我们看到的const变量取地址时,取的地址实际上是匿名变量的地址,就算我们通过指针修改了该匿名变量的值,我们也不能影响到真正的const变量的值。因为这个符号对应的值存储在符号表中。用常量定义数组
下列代码功能能否正常实现?int a=3; int b=4; int array[a+b]={0};//错误 数组是固定内存块大小的别名。a+b是个变量,大小不确定,这跟数组的概念相违背。 //正确实现 #define N 20 int array[N];
练习:利用const关键字定义常指针,指向常量的指针,指向常量的常指针。
- const和define的区别
- const在编译的时候实现,define在预编译的时候实现。
- define没有作用域的限制(因为预编译只是简单的展开,对整个代码都做了一次替换;而const只在当前作用域应用)
enum
C++中的枚举数据类型更加的便于使用。enum season{ SPR, SUM, AUT, WIN }; C: enum season s=SPR; s=0; s=1; s=2; 问题: 在很远的地方,记不住012分别表示什么怎么办?这种表示方法很不方便 C++中: 用enum中的元素直接赋值,不能用int类型数据。 这是为了enum数据类型的可读性。不用再int类型和值之间的转换了。
C++引用
- 引用的基本概念
- 引用的本质
- 引用的功能
- 引用作为函数参数
- 引用作为函数返回值
- 指针引用
- const 引用
什么是引用
在C中我们想要访问某一内存空间有两种办法,直接法和间接法。直接法是直接使用变量名来操作内存空间,间接法是利用指针间接操作内存空间。C++中,提供了一种操作内存的新方法引用,通过引用我们能直接操作内存空间,并且能够“无缝切换”跨函数操作内存。
简单来说,引用就是变量的别名。通过变量名我们能够访问内存空间,通过引用我们也能够访问该内存空间。定义
int a=10; int &b=a; //这里用取地址符&来定义一个引用。取地址符前加上数据类型表明定义一个引用,其余情况均为取地址 int &c; //错误,定义一个引用必须将其初始化,否则引用将不知道它引用的是谁而报错。因为引用的本质是一个常指针,占用4个字节的内存空间。 int &d=b; //引用可以嵌套引用,这个时候变量a ,引用b, 引用d表示的是同一个内存空间。 int q=30; b=q; //这里是让引用b引用变量q还是讲变量q的值传给引用b所代表的内存空间?(a=30,b=30)
- 为什么要引入引用
在C中,我们见到过多级指针的使用。往往需要仔细的分析才能合理的传参数以及分配内存。指针是一个麻烦的东西,使用指针的封装–引用,我们能够避免过多的与指针纠缠。同时引用具有良好的可读性,更适合开发。但是引用本身的设计也常常被人诟病,什么事情都是一把双刃剑吧。
引用的本质
引用的本质就是一个封装的常指针。int a=10; int &b=a; int * const c=&a; cout<<*c<<" "<<b<<endl; //*c 和b表示的是同一内存空间
引用的常见应用
引用做函数参数
函数参数常见取值:传值,传指针,传引用
传引用最常见的一个应用就是值的互换。这里就不再赘述了。
这里需要提的一点就是引用做函数参数实际上是引用做左值的一个特例。函数的实参可以是变量,也可以是引用。如果是变量,这是变量初始化引用,该引用是变量所代表内存空间的别名;如果是引用,这是引用a初始化引用b,引用b代表的内存空间和引用a所代表的内存空间相同。引用做函数返回值
引用做函数返回值本质上是引用做右值的一个特例。如果是引用给变量赋值,则是值拷贝。如果是引用a给引用b赋值,则是将引用a所代表的内存空间起一个别名b。- 引用做函数返回值可以做左值
例子:三目运算符,三目运算符返回的是一个引用。
- 引用做函数返回值可以做左值
const的引用
如果要引用一个const常量,那必须用const引用去引用它。const引用可以引用一个非const的变量。
const引用一般用在形参上,来限制被引用的变量不能够被修改。
const不能保证所引用的变量的值不被修改。但是能保证无法通过const定义的引用来修改变量。
const引用引用一个字面量时,会临时开辟一个内存空间,存放字面量。例如:const int &re =10
会临时开辟一块存储空间存放10,re引用的变量是该临时内存空间。- 指针的引用
常见的指针引用的形式为int * &p
,那么为什么要使用指针的引用呢?我们知道,我们想要跨函数修改一级指针的值时,需要传递二级指针做函数的参数。如果使用指针引用则避免了1级指针到二级指针再到一级指针的转换。