首先C++是C语言的延伸以及拓展,所以C++兼容C语言的绝大多数语法,因此在C++中,下面的这段C语言代码是一样能够运行,但这个是C语言风格的“hello world”。
#include<stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
而C++风格的“hello world”应该是这种风格的。
#include<iostream>
using namespace std;int main()
{
cout << "hello world\n" << endl;
return 0;
}
接下来我们从C++增加的几个部分开始介绍。
目录
一、命名空间
1、namespace的定义
2、命名空间的使用
二、输入输出流
三、缺省参数
四、函数重载
五、引用
六、引用和指针的关系
七、inline
八、nullptr
一、命名空间
我们知道在一个作用域中,不能存在同名的变量或者函数,而在C语言中,有很多的变量和函数都是作用在全局作用域上的,也就是整个工程文件中的,这样会造成很多的冲突。为了解决这个问题,C++引入了namespace(命名空间)。
1、namespace的定义
定义一个namespace只需要用namespace后面加上一对{}即可,在这对{}中就可以定义变量、函数、类了。namespace的本质是定义出了一块和全局域独立的域,在不同的域中可以定义同名的域,这样同名的冲突就解决了。有了namespace以后,在编译时,编译器就会先从对应的域中查找是否存在这个成员,如果存在,就使用对应域中的成员;如果不存在再到全局域中查找。C++中有全局域、局部域、命名空间域,类域。全局域和局部域不仅会影响编译的查找逻辑,还会影响变量的生命周期,而命名空间域和类域只会影响编译的查找逻辑。namespace只能定义在全局,同时支持嵌套定义。当一个文件中有多个同名的namespace时,会被自动识别为一个namespace,不会发生冲突。C++标准库都放在一个叫“std”的命名空间中。
2、命名空间的使用
1、使用using展开命名空间,比如:
using namespace std;
这个段代码就是展开了C++标准库的命名空间,这样我们在工程文件中使用一个变量时,会先从C++标准库中进行查找。
2、使用域作用限定符
域作用限定符就是“::”,在某个变量或者函数之前加上命名空间和::,就可以在编译时先到这个命名空间中查找这个变量或者函数,如:
#include<iostream>int main()
{std::cout << "hello world\n" << std::endl;return 0;
}
二、输入输出流
<iostream> 是 Input Output Stream 的缩写,是标准的输⼊、输出流库,定义了标准的输⼊、输出对象。std::cin 是 istream 类的对象,它主要⾯向窄字符的标准输⼊流。std::cout 是 ostream 类的对象,它主要⾯向窄字符的标准输出流。std::endl 是⼀个函数,流插⼊输出时,相当于插⼊⼀个换⾏字符加刷新缓冲区。<<是流插⼊运算符,>>是流提取运算符。在C++中使用输入和输出时更方便,不像C语言中需要手动指定格式,C++中的cin和cout可以自动识别格式。
三、缺省参数
缺省参数就是在声明或者定义一个函数时,为函数的参数指定一个缺省值,当在调用这个参数时,如果没有指定实参,则采用这个缺省值,否则使用指定的实参,缺省参数分为全缺省和半缺省参数。全缺省参数就是全部形参都给定缺省值,而半缺省参数就是部分的形参给定了缺省值,半缺省参数必须从右往左依次连续的给定缺省值,中间不能跳跃。在调用半缺省参数时,从左往右依次连续给定实参,中间不能跳跃。同时在函数声明和定义分离时,缺省参数不能在声明和定义中同时出现,规定必须函数声明给缺省值。
四、函数重载
函数重载就是在同一作用域中出现了同名的函数,但是这些同名函数的形参不同,可以是形参的个数,也可以是参数类型不同,还可以是参数类型的顺序不同。函数的返回值以及参数的命名不能作为函数重载的条件。
//1、参数类型不同
int add(int x, int y)
{return x + y;
}double add(double x,double y)
{return x + y;
}//2、参数类型顺序不同
int add(int x, double y)
{return x + y;
}int add(double x, int y)
{return x + y;
}//3、参数个数不同
int add(int x, int y)
{return x + y;
}int add(int x, int y, int z)
{return x + y + z;
}//返回值不同不能构成函数重载
int add(int x, int y)
{return x + y;
}double add(int x, int y)
{return x + y;
}//参数命名不同不能构成函数重载
int add(int x, int y)
{return x + y;
}int add(int y, int x)
{return x + y;
}
五、引用
C++中引用的作用是给变量取一个别名,它和指正最大的区别就在于,因为是取别名,所以编译器不会为引用变量申请一块内存空间,也就是说一个变量和它的引用变量时共用一块内存空间的;而指针则需要单独申请一块空间用来存放指针。
引用的用法是:类型& 引⽤别名 = 引⽤对象。
引用有以下几个比较重要的特性:
1、引用在定义时必须初始化。
2、一个变量可以有多个引用。
3、引用一旦引用了一个实体,就不能再引用别的实体,也就是引用的指向无法改变。
4、在引用const对象时,必须用const引用。同时在引用对象时可以将对象的访问权限缩小,而不能放大。
下面的代码中的引用中,只有第一个是正确的,后面的三个都是错误的。这里错误的原因就和前面的第四点中提到的权限的放大和缩小有关系了。因为在C++中表达式的结果、函数的返回值以及类型转化后的结果都会保存在一个临时对象中,而在C++中规定临时对象具有常性,不能改变,所以这里的问题就是把权限放大了,所以出现了错误。(int& w=c中有一个隐式的类型转化,因为c是double类型的数据而w是int类型的引用,这里编译器自动把double类型的c转化成了int类型)
int add(int x, int y)
{return x + y;
}int main()
{int a = 8;int b = 1;double c = 1.0;//正确int& x = a;//错误int& y = add(a, b);int& z = a * 8;int& w = c;return 0;
}
六、引用和指针的关系
概念上引用是给变量取一个别名不开空间,指针是存储一个变量地址,要开空间。
引用在定义时必须初始化,指针在定义时不一定要初始化。
引用在引用一个对象后不能再引用其他对象,即指向不能改变。而指针可以改变指向的对象。
访问一个变量的引用就相当于直接访问这个变量,而指针需要在解引用以后才是访问指向的对象。
sizeof引用的结果为引用的数据类型的大小,而sizeof指针则是4/8个字节。
指针很容易出现空指针和野指针的问题,引用则很少出现。(很少出现不代表不会出现空引用的问题)
七、inline
⽤inline修饰的函数叫做内联函数,编译时C++编译器会在调⽤的地⽅展开内联函数,这样调⽤内联函数就需要建⽴栈帧了,就可以提⾼效率。inline对于编译器⽽⾔只是⼀个建议,也就是说,你加了inline编译器也可以选择在调⽤的地⽅不展开,不同编译器关于inline什么情况展开各不相同,因为C++标准没有规定这个。inline适⽤于频繁调⽤的短⼩函数,对于递归函数,代码相对多⼀些的函数,加上inline也会被编译器忽略。
八、nullptr
C语言中NULL可能被定义为字⾯常量0,也可被定义为⽆类型指针(void*)的常量。因为C++中存在函数重载,若出现以下情况:
void func(void*)
{}
void func(int)
{}int main()
{func(NULL);return 0;
}
如果这个时候如果用NULL表示空指针,编译器会调用形参为int类型的函数,并不符合我们的要求,我想要的是NULL表示空指针,所以这时候引入了nullptr,nullptr是⼀种特殊类型的字⾯量,它可以转换成任意其他类型的指针类型。使⽤nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被隐式地转换为指针类型,⽽不能被转换为整数类型。