1.c++关键字
2.命名空间
3.c++输入和输出
4.缺省参数
5.函数重载
6.引用
7.内联函数
8.auto关键字(c++11)
9.基于范围的for循环(c++11)
10.指针空值----nullptr(c++11)
0.前言
c++是在c的基础上,容纳进去了面向对象编程思想,并增加了许多有用的库,以及编程范式。
学习目标:
1.c++补充c语言语法的不足,以及c++是如何对c语言设计不合理的地方进行优化的,如:作用域方面,IO方面,函数方面,指针方面,宏方面等。
2.为后续类和对象学习打基础
1. c++关键字(c++98)
c++总计63个关键字,c语言32个关键字
2. 命名空间
在c/c++中,变量,函数和后面学到的类都是大量存在的,这些变量,函数和类的名称将都存在于全局作用域中,可能会导致很多冲突,使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。
#include<stdio.h>
#include<stdlib.h>
int main()
{
//c语言无法解决这样的命名冲突问题,
//所以c++提出了namespace来解决
int rand=10;
printf("%d\n",rand);
return 0;
//编译后报错:"rand":重定义;以前的定义是"函数"
}
2.1命名空间的定义
定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。
//1.命名空间的正常定义
//a是命名空间的名字
namespace a
{
//命名空间可以定义变量,函数,结构体
int rand=10;
int Add(int left,int right)
{
return left+right;
}
struct list
{
struct list*next;
int val;
};
}
//2.命名空间可以嵌套
//test.cpp
namespace N1
{
int a;
int b;
int Add(int left, int right)
{
return left+right;
}
namespace N2
{
int Sub(int left,int right)
{
return left-right;
}
}
//3.同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中
//一个工程中的test.cpp和test.h 中的N1会被合并成一个
//test.h
namespace N1
{
int Mul(int left,int right)
{
return left*right;
}
}
注意:一个命名空间就定义了一个新的作用域,命名空间中所有内容都局限于该命名空间中。
2.2命名空间的使用
命名空间中的成员该如何使用?
如果直接使用:
namespace bit
{
int a=0;
int b=2;
int Add(int left,int right)
{
return left+right;
}
}
int main()
{
printf("%d\n",a);
// 编译报错:"a"未声明的标识符
return 0;
}
命名空间的三种使用方式:
★加命名空间名称及作用域限定符
int main()
{
printf("%d\n",bit::a);
return 0;
}
★使用using将命名空间中某个成员引进
using bit::b;
int main()
{
printf("%d\n",bit::a);
printf("%d\n",b);
return 0;
}
★使用using namespace命名空间名称引入
using namespace bit;
int main()
{
printf("%d\n",bit::a);
printf("%d\n",b);
Add(10,20);
return 0;
}
3.C++输入和输出
#include<iostream>
//std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中
using namespace std;
int main()
{
cout<<"Hello world!!!"<<endl
return 0;
}
使用:
1.使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含<iostream>头文件以及按命名空间使用方法使用std。
2.cout和cin是全局的流对象,endl是特殊的c++符号,表示换行输出,他们都包含在<iostream>头文件中。
3.<<是流插入运算符,>>是流提取运算符。
4.使用c++输入输出更方便,不需要像printf/scanf输入输出需要手动控制格式。c++的输入输出可以自动识别变量类型。
5.实际上cout和cin分别是ostream和iostream类型的对象,>>和<<也涉及运算符重载等知识。
注意:早期标准库将所有功能在全局域中实现,声明在.h后缀头文件中,使用只需要包含对应的头文件,后来将其实现在std命名的空间下,为了和c头文件区分,也为了正确使用命名空间,规定c++头文件不使用.h;旧编译器(vc 6.0)中还支持<iostream.h>的格式,后续编译器已不支持,推荐使用<iostream>+std的方式。
#include<iostream>
using namespace std;
int main()
{
int a;
double b;
char c;
cin>>a;
cin>>b>>c;
cout<<a<<endl;
cout<<b<<" "<<c<<endl;
return 0;
}
std命名空间的使用惯例:
std是c++标准库的命名空间,如何展开std更合理?
1.在日常使用中,直接使用using namespace std就可以,这样很方便
2.但是using namespace std展开,标准库就全部暴露出来,如果我们定义跟库重名的类型/对象/函数,就存在冲突问题,该问题在日常练习中很少出现,但是项目开发的代码较多,规模大,就很容易出现。像std::cout这样使用时指定命名空间 + using std::cout展开常用的库对象/类型等方式。
4. 缺省函数
4.1缺省函数的概念
缺省函数就是在声明或定义函数时为参数指定一个缺省值,在调用该函数时,如果没有指定实参,则直接使用该缺省值,否则使用指定的参数。
void func(int a=0)
{
cout<<a<<endl;
}
int main()
{
func();//没有传参时使用默认值
func(10);//传参时,使用默认实参
return 0;
}
4.2缺省参数分类
★全省参数
void func(int a=1,int b=4,int c=10)
{
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
cout<<"c="<<c<<endl;
}
★半缺省参数
void func(int a,int b=3,int c=10)
{
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
cout<<"c="<<c<<endl;
}
注意:
1.半缺省参数必须从右往左依次给,不能隔着给
2.缺省参数不能在声明和定义中同时出现
3.缺省值必须是常量或者是全局变量
4.c语言不支持
//a.h
void func(int a=10);
//a.cpp
void func(int a=10)
{
;
}
//注意:
如果声明和定义同时出现,恰巧两个地方给的值不相同,那么编译器就无法确定用哪个缺省值
5.函数重载
5.1函数重载的概念
函数重载:是函数的一种特殊情况,c++允许在同一个作用域中声明几个作用类似的同名函数,这些同名函数参数列表(参数类型或参数个数或类型顺序)不同,常用来处理功能类似,数据类型不同的问题。
#include <iostream>
using namespace std;
//参数类型不同
int Add(int x,int y)
{
return x+y;
}
double Add(double x,double y)
{
return x+y;
}
//参数个数不同
void fun()
{
cout<<"void fun()<<endl;
}
void fun(int x)
{
cout<<"void fun(int x)"<<endl;
}
//参数类型顺序不同
void fun(int x,double y)
{
cout<<void fun(int x,double y)<<endl;
}
void fun(double x,int y)
{
cout<<void fun(double x,int y)<<endl;
}
int main()
{
Add(3,4);
Add(5.1,6.7);
fun();
fun(3);
fun(2,4.5);
fun(6.3,7);
return 0;
}
5.2 c++支持函数重载的原理--名字修饰
在c/c++中,程序要运行起来要经历:预处理,编译,汇编,链接
1.实际项目中通常有多个头文件和多个源文件共同构成,由编译链接可知,当a.o中调用b.o中的Add函数是,编译后链接前,a.o的目标文件中找不到Add的地址,由于Add在b.o中定义,所以Add的地址在b.o中。
2.链接阶段就解决这个问题,链接器看到a.o中调用了Add,但是没有Add的地址,就会到b.o中的符号表中寻找Add的地址,然后链接在一起。
3.链接时,面对Add函数,链接器根据名字去寻找,每个编译器都有自己的函数名修饰规则
4. 由于Windows下vs的修饰规则过于复杂,而Linux下g++的修饰规则简单易懂,下面用gcc演示
int Add(int a,int b)
{
return a+b;
}
void func(int a,double b,int*c)
{
}
int main
{
Add(1,2);
func(1,2,0);
return 0;
}
5. 通过下面我们可以看出gcc的函数修饰后名字不变。而g++的函数修饰后变成【_Z+函数长度+函数名+类型首字母]
采用c语言编译器编译的结果如下
结论:在linux下,采用gcc编译完成后,函数的名字修饰没有改变
采用c++编译器编译的结果如下
结论:在linux下,采用gcc编译完成后函数的名字发生改变,编译器将函数参数类型信息添加到修改后的名字中
6. 通过这里就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
7. 如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办法区分。