1. C++中关键字
2. 命名空间:概念以及使用方式
3. 缺省参数:概念、分类、注意事项
4. 函数重载:
函数重载概念、函数重载调用原理、C语言支持函数重载吗?为什么? extern "C"的作用
5. 引用:
C语言中函数有几种传参方式?优缺点
引用的概念以及特性
const类型引用
引用的使用场景
传值、地址、引用效率方面的比较
引用和指针的区别
6. 内联函数
内联函数的概念、特性以及注意事项
总结内联函数与宏函数的区别
7. C++11语法糖
auto类型推导
范围for循环
控制指针nullptr
C++中的关键字: 98 版本C++关键字有63个。不同版本有所不同。
命名空间:因为C / C++中要命名大量的变量、函数名和类的名称,容易造成命名冲突或名字污染,所以提出命名空间,来解决这些问题。
(理解成在逻辑上将名字的保存空间划分成不同区域。)。
命名空间以namespace关键字来定义,后面跟命名空间的名字,然后接一对{}。
定义命名空间有三种方式 : 普通命名空间 嵌套命名空间 同名命名空间合并。
普通命名空间
namespace Name1 //Name是命名空间的名字
{
命名空间中乐意定义变量、函数
int a;
int Sum(itn x, int y)
{
return x + y;
}
}
嵌套命名空间
namespace A1
{
int a;
int b;
int test();
namespace A2
{
int x;
int y;
}
}
同工程中的同名命名空间,编译器会将其合并。
namespace N
{
int a;
int Add(int n, int m)
{
return n + m;
}
}
namespace N
{
int x;
int y;
}
命名空间的使用方式:1 域名加作用域限定符 N::a
2 using将命名空间成员引入 using N1::a
3 using namespace将命名空间引入 using namespace N
namespace N1
{
int a;
int b;
int Add(int n, int m)
{
return n + m;
}
}
1 域名加作用域限定符 N1::a
void main()
{
printf("%d
", N::a);
}
2 using将命名空间成员引入 using N1::a
using N1::b;
void main()
{
printf("%d
", b);
}
3 using namespace将命名空间引入 using namespace N
using namespace N1;
void main()
{
int c=Add(1, 2);
printf("%d
", c);
}
缺省参数:声明或定义函数时设置的默认值,没用实参时函数用该默认值作为参数值,有实参则用实参。
全缺省参数:函数中所有参数都带有默认值。
全缺省参数函数传值时从从前往后传参,没有实参的就用默认值作参数值。
半缺省参数:部分参数带有默认值,函数参数列表从右往左给默认值,从左往右传参。
缺省值不能同时在声明和定义中给出,必须单独在声明或定义其中一个给出。
缺省值只能是常量或者全局变量。
全缺省
void fun(int a = 2, int b = 3, int c = 4, int d = 4);
{
}
板缺省 必须从左往右依次设默认值,不能间隔着给默认值。
void fun2(int a , int b , int c = 4, int d = 4);
{
}
void fun2(int a , int b=4 , int c , int d = 4); //错误
void fun2(int a=2, int b =4, int c, int d ) //错误
函数重载 :一个函数名定义多个功能相同,形参列表不同的函数,用来处理功类似但数据类型或要求不同的问题。
C++中 同一作用域中几个功能类似的同名函数,形参列表不同(参数类型、个数、顺序不同都可以)。
int Add(int a, int b)
{
return a + b;
}
double Add(double a, double b)
{
return a + b;
}
函数重载:必须在相同的作用域, 相同名字的函数,参数列表不同。与返回值类型是否相同无关。
编译器在编译阶段,编译器对实参的类型进行推演, 根据推演的实参类型的结果选择调用合适的函数。
C语言不支持重载,因为C语言编译时对函数名的处理是加_ 如Add函数处理成 _Add,不管函数参数类型。
如果C语言中函数名重复,那么系统将无法区分。
C++能重载是因为C++在编译时,将函数类型和函数名一起处理区分。
extern"C"的作用是将函数按照C语言的规则进行编译。
void TestFunc()
{}
2与1是参数个数不同
2
void TestFunc(int a)
{}
3
void TestFunc(int a, double b)
{}
3与4不同:参数类型次序
4
void TestFunc(double a, int b)
{}
2和5参数的类型
5
int TestFunc(char a)
{
return 0;
}
6
char TestFunc(char a)
{
return a;
}
int main()
{
int ret1 = TestFunc('a'); ///编译报错,返回值的类型不能构成重载条件,必须是参数列表不同。
char ret2 = TestFunc('b');
TestFunc('c');
return 0;
}
引用:給已经定义的变量再定义一个名字,引用变量不开辟内存空间,和原变量是一个内存空间(小名和学名是同一个人)。
引用类型必须和原变量(引用实体)的类型相同 (学名和小名的不能把一个人变成妖怪)。
引用定义是必须初始化 (取别名可定是针对已经存在的人)。
一个变量可以有多个引用 (人在不同的地方,周围的人对他的称呼可以有很多种 )。
(一个作用域中)引用一旦一个实体,不能再引用其它实体 (女朋友叫你男朋友,不能再叫别人男朋友。哇哈哈哈)。
类型& 名字 = 原变量名
void testRef()
{
int a = 5;
int& ra=a;
const int& ra2 = a; //不能通过ra2来修改a (参考const修饰指针的规则)
const int b = 3;
int& rb = b; //编译报错,const int 无法转化为 int&
const int c = 4;
const int& rc = c; //编译通过,所以const修饰的变量引用时也要用const修饰。
const int& rrc = rc; //编译通过,可以对引用进行引用操作。
const int& rc = b; //错误
int& rc = a; //错误 一个引用只能对一个实体(变量)进行引用。
}
void testRef()
{
const int a = 3;
const int& ra = a;
const int& rra=ra; //可以对引用再次引用。
cout << &a << " " << &ra << " " << &rra << endl;
double b = 12.21;
}
int main()
{
testRef();
system("pause");
return 0;
}
引用的应用场景
引用可以做函数参数、返回值
void Swap(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
int& Add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
int a = 3;
int b = 5;
int& ret = Add(a, b);
printf("%d
",ret);
Add(1, 2);
cout << ret << endl; //ret的值变成3,是因为函数调用时会将返回值传到这个空间。
system("pause"); //不能用栈上的空间作为引用类型返回。
return 0;
}
传值、传引用
传值作为函数的参数或返回值时,不会直接传递实参或返回变量本身,而是将实参或变量临时拷贝一份传递,效率很低。
引用作为函数的形参:如果想要通过形参改变外部实参---传递普通类型引用。
如果不想要通过形参改变外部实参---传递const类型引用。
引用作为函数的返回值类型。
如果按照引用方式进行返回,一定不能返回栈上的空间。
返回变量的生命周期不受函数结束的影响。
传引用和指针的运行效率是差不多的,二者都比传值的效率高很多,
引用不开辟空间,但是底层中,引用的处理方式就是指针的处理方式。
引用与指针的区别:引用不开辟新空间,指针要开辟自己的空间。
引用必须初始化,指针可以不初始化。
引用在引用一个实体后不能再引用其它实体,而指针可以随时修改所指向的同类型变量。
没有空引用,有空指针。
sizeof中引用的大小是所引用的实体的类型的大小,而指针是一个地址空间所占字节的大小。
引用加1是引用的实体加1,指针加1是指针所指位置往后偏移一个所指变量的类型字节数。
有多级指针,没有多级引用 (c++11 右引用?)
访问实体时指针要解引用,而引用是编译器自己处理。
引用比指针更安全
内联函数
用inline修饰的函数,编译时在调用内联函数的地方展开,不用压栈。内联函数是以空间换时间,提升程序运行效率。
代码很长或者有循环递归时不适合用内联函数, 内联函数 inline不要将声明和定义分离,inline被展开红藕函数没有地址,会导致链接错误
inline void Swap(int a, int b);
内联函数与宏函数的区别:内联函数做参数类型检查,而宏函数不做参数类型检查
内联函数不用中断调用,编译时可以镶嵌到目标代码中,而宏只是简单地字符替换
宏的优点:增强代码复用性,一改全改提高性能,提高性能。
宏的缺点:不方便调试,因为编译时进行了字符替换,导致代码可读性差,可维护性差,容易误用,没有类型安全检查
C++中替代宏的方法:常量定义加const修饰
函数用内联函数
C++11中的 auto 关键字
auto 修饰的变量,编译器根据初始化表达式来推导变量的实际类型,auto 是类型声明的“占位符”。而不是类型声明。
(理解为帮同学占个座位,来的是哪个宿舍的同学就说这儿坐的是哪个宿舍的人),编译后将auto换为实际类型。
auto 使用细则:1、与指针和引用结合使用时,auto和auto* 没有区别, auto& 则必须加上 &
2、在同一行定义多个变量,必须是同类型,编译器根据第一个变量值来进行推导,用推导出来的类型定义后面的变量
3、auto 定义的变量必须初始换,根据初始化值来推导,没有初始化则无法推导。
auto不能推导的情况: 不能做为函数的参数,编译器无法对函数实参的类型进行推导。
不能用来声明数组。
基于范围的 for 循环 (C++11)
int main()
{
int arr[4] = { 1, 2, 3, 4 };
for (int i = 0; i < 4; ++i)
{
cout << arr[i] << " ";
}
cout << "
";
for 循环语法 auto& e 表示定义一个用于迭代的变量,第二部分表示迭代的范围(此处为数组arr)
可以用 continue 结束本次循环 ,也可以用 break 跳出整个循环
for (auto& e : arr)
cout << e<< " ";
system("pause");
return 0;
}
范围for的使用条件:范围确定 迭代的对象要实现++或者==的操作
指针空值用 nullptr (C++11)
int *p = nullptr