C是C++的子集,在C语言的基础上添加了面向对象的特性,以及C++标准库。
C++总计63个关键字,C语言32个关键字
定义命名空间,我们用到一个新的关键字namespace,这个关键字后是命名空间的名字,然后用一个{},{}内的是命名空间的成员。
// 关键字 名字
namespace u1
{
int a; // 一个普通的int型变量
// 也可以是一个函数
int add(int a,int b)
{
return a + b;
}
}
namespace u2
{
// 命名空间里面也可以嵌套命名空间
namespace u3
{
int d1;
int d2;
}
}
// 如果说同一个工程下的不同文件,都有相同的命名空间,编译器最后会将其合并
// test1.cpp
namespace u4
{
int add(int a,int b)
return a + b;
}
// test2.cpp
namespace u4
{
int mul(int a,int b)
return a * b;
}
// 最后这两个函数都是在u4这个命名空间下,不会冲突
注:命名空间其实相当于一个作用域,命名空间{}内的内容都是在这个空间里面,我们c++的标准库都在std这个命名空间下,如果想使用c++标准库的函数,容器,需要在前面加上用std这个命名空间
命名空间的使用方法有三种:
(1)
// 1.命名空间名称及作用域限定符
int main()
{
printf("%d\n",u1::a);
return 0;
}
(2)
// 使用using将命名空间中成员引入
using namespace u1::a; // 意思就是讲u1下的a变量展开,这时候它的作用域就是全局作用域
int main()
{
printf("%d\n",a); // 这时候不用加命名空间和作用域限定符
return 0;
}
(3)
using namespace u1; // 意思是将u1整个命名空间展开
int main()
{
printf("%d\n",a);
return 0;
}
这是一个最简单的C++程序,里面包含了一个头文件iostream,一个main函数以及一句打印函数,我们看到在cout这个函数前面,加上了一个std::,这是因为iostream库是属于标准库的一块,在std这个命名空间下。
标准输入输出对象
标准库定义了4个IO对象,IO(输入输出),以下:
一个C++程序
(1)输出
// 编写一个简单的c++程序
#include // C++标准库的iostream库,包含了c++的输入和输出函数
int main()
{
// 打印hello world
std::cout << "hello world" << std::endl;
}
(2)输入
#include
int main()
{
int a,b;
std::cin >> a >> b; // 输入a和b
std::cout << a << " " << b << std::endl;
return 0;
}
注:如果不想每次在cin或者cout前加上std::,可以使用前面讲的命名空间使用方法,很多人会使用using namespace std;,但是建议不要去这样做,因为c++标准库的内容都在std这个命名空间下,如果使用这句语句,会将标准库的内容暴露出来,那么像cout或者cin这些即常用又不像使用std::,可以使用以下方法。
using namespace std::cout;
using namespace std::cin;
缺省参数是声明或者定义一个函数,为参数指定一个默认值。
#include
using namespace std::cout;
using namespace std::cin;
int fun(int a = 0)
{
return a;
}
int main()
{
cout << fun(); // 0
cout << fun(1); // 1
return 0;
}
带默认值的参数,如果在传参时,没有传入指定参数,则该函数会使用这个缺省参数。
// ... 头文件
// 全缺省参数
int add(int a = 0, int b = 1)
{
return a + b;
}
int main()
{
cout << add() << endl; // 结果为1
cout << add(1, 2) << endl; // 结果为3
cout << add(2) << endl; // 结果为3
//add(, 1); 这个是错的,不能跳过第一个参数,传给第二个
return 0;
}
// ... 头文件
// 半缺省参数
int add(int a, int b = 1)
{
return a + b;
}
int main()
{
cout << add() << endl; // 错误的,因为a没有默认参数
cout << add(1, 2) << endl; // 结果为3
cout << add(2) << endl; // 结果为3
return 0;
}
总结:
C++允许函数在统一作用域下出现同名函数,这些同名参数的参数个数或类型或顺序可以不同。
int add(int a,int b)
{
return a + b;
}
int add(double a,double b)
{
return a + b;
}
int add(int a,int b,int c)
{
return a + b + c;
}
// 这里会发生函数重载失败
double add(double a,double b)
{
return a + b;
}
注:只有参数个数和类型还有顺序不同才可以发生重载,如果只是返回值不一样,编译器会认为是同一个函数。
引用的含义是给一个变量取一个别名,它是和引用的变量共享同一块空间。
int main()
{
// 类型& 引用变量名 = 引用对象
int a = 10;
int& b = a; // b是a的别名
cout << a << " " << b;
return 0;
}
结果都是10,但要注意一点,引用对象和别名的类型都要属于同一种类型。
int main()
{
int a = 10;
// int& ra; // 这条会报错
int& ra = a;
int& rra = a;
int b = 20;
ra = b; // 这句话不是修改引用对象,而是赋值
return 0;
}
int main()
{
const int a = 10;
// int& ra = a; // 这里会报错
const int&ra = a;
int b = 10;
const int& rb = b; // 这里没问题
}
结论:
在c语言中,如果想写一个交换函数,我们需要通过指针传地址,将其修改,在c++中,我们可以使用引用,引用本质上其实也是传地址的一种方式,但是引用更具有可读性。
int swap(int &a,int &b)
{
int tmp = a;
a = b;
b = tmp;
}
下面这段代码会输出什么,如果不加static会怎么样
int& Count()
{
static int n = 0;
n++;
// ...
return n;
}
我们来看下非static的情况,这时的n是一个局部变量,我们知道局部变量出了函数作用域就会销毁,那这时候将一个局部变量进行引用返回,这个内存空间其实已经还给系统了。虽然第一次能够成功打印出结果,但是第二次是一个随机值。这不是我们想要的。
int& Count()
{
int n = 0;
n++;
// ...
return n;
}
但如果是加上static的话,会是这个局部变量变为全局变量,全局变量的作用域是一直到整个程序结束。
C语言中,我们在进行传参的时候,会考虑使用两种传参方式,一种是传值,一种是传地址,其实c++的引用本质上就是传地址的方式。参考下面的代码,我们在传递一个数组的时候,如果采用传值的方式,会创建一个临时的拷贝,效率是非常低的,尤其是传入的参数特别大的时候。
#include
struct A{ int a[10000]; };
void TestFunc1(A a){}
void TestFunc2(A& a){}
void TestRefAndValue()
{
A a;
// 以值作为函数参数
size_t begin1 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc1(a);
size_t end1 = clock();
// 以引用作为函数参数
size_t begin2 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc2(a);
size_t end2 = clock();
// 分别计算两个函数运行结束后的时间
cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}
建议:尽可能使用const引用,我们前面知道进行引用传递的效率比传值效率高,但是,在有些场景下,我们并不想修改传入参数的值,这个时候可以使用const引用传址。