一、C/C++关键字
- C++98标准下C++共63个关键字、 C99标准下C语言共32个关键字
二、命名空间
-
概念:C++中有很多函数、变量、类的名字都是大量存在于全局命名空间的,为了避免命名冲突和名字污染,使用命名空间对标识符的名称进行本地化。
-
作用:避免名字污染
-
定义:
namespace N1
{
1.变量
2.函数
3.命名空间(嵌套使用)
}
-
同一工程可以使用命名相同的命名空间(使用时编译器会合并使用)
-
使用
-
一个命名空间相当于一个新的作用域
-
namespace:命名空间关键字 : : 是作用域解析符
-
N1::变量;
-
using N1::变量;
-
using namespace N1;
-
若函数内部的命名空间的变量与函数内部变量冲突,可用N::变量解决
-
-
三、C++输入输出
-
输入输出标准流
-
标准输入流 cin
-
标准输出流 cout
-
-
#include<iosteam.h>旧版本,新版本不支持
-
#include<iosteam>新版本,C++标准所支持的,需要与命名空间std一起使用( using namespace std;)
-
标准输入
-
cin>>a
-
-
标准输出
-
cout<<“C++”<<endl;
-
支持连续输出
-
不用控制格式,系统会自动识别格式
-
cout<<hex<<a<<endl(十六进制hex)
-
四、参数列表
-
C语言中没有指定参数列表,默认可接收任意多个参数,C++中,有严格的参数检测,没有参数列表的函数默认为void不接受参数
-
缺省参数列表
缺省参数是函数在定义或声明时,为函数的参数指定一个默认值,调用该函数时若没有指定的实参则使用该默认值,若有实参则使用实参
-
半缺省参数列表
-
声明或定义时,函数的部分参数都带有缺省值
-
void Test(int a,int b=2,int c=3) //缺省值从右向左依次给值
{
cout<<"a"<<" "<<"b"<<" "<<"c"<<endl;
}
int main()
{
Test(10,20,30); //输出结果为10 20 30
Test(10,20); //输出结果为10 20 3
Test(10); //输出结果为10 2 3
Test(); //error
}
全缺省参数列表
- 声明或定义时,函数的所有参数都带有缺省值
void Test(int a=1 , int b=2 , int c=3)
{
cout<<"a"<<" "<<"b"<<" "<<"c"<<endl;
}
int main()
{
Test(10,20,30); //输出结果为10 20 30
Test(10,20); //输出结果为10 20 3
Test(10); //输出结果为10 2 3
Test(); //输出结果为1 2 3
}
函数缺省参数规则
- 半缺省参数必须从右往左依次来提供,不能间隔着给出
-
函数的调用约定(-cdel)规定函数传参从右向左等
-
- 缺省参数不能同时在函数声明和定义中出现,只能二者择其一
-
一般情况下函数的缺省参数在声明中,便于实现程序的封装以及信息的隐蔽性
-
- 缺省值必须是常量或者全局变量
- C语言不支持
五、函数重载
-
概念:函数重载指C++中允许同一作用域内声明多个命名相同形参列表(参数个数、类型、顺序)不同的函数,函数重载常用于处理实现功能类似数据类型不同的问题
-
函数重载的条件:
- 相同的作用域
- 全局作用域
- 函数内部局部作用域
- 命名空间作用域
- 类作用域
-
函数名必须相同
-
参数列表必须相同
-
仅函数返回值不同,不能构成重载
几个典型函数重载的例子
//error函数调用不明确(二义性)
int Test(int a, int b)
{ }
double Test(double a, double b)
{ }
int main()
{
Test(10.0 , 20); //error:两个重载函数有相似的类型转换(编译器无法判断调用哪个函数)
return 0;
}
参数个数不同的重载
void Func( );
void Func( int a);
参数类型不同的重载
void Func(char a );
void Func( int a);
参数个数和类型均不同的重载
void Func(char a );
void Func( int a , char b);
参数顺序不同的重载
void Func(char a , int b);
void Func( int a , char b);
名字修饰(Name Mangling)
C++程序在编译过程中使用名字修饰将函数、变量的名称重新改编,以区分各个函数,将函数通过一定算法修饰为一个全局唯一的名称。
C语言的名字修饰规则只是在函数名字前面添加了下划线。
-
为什么C语言不能支持函数重载?&&C++中函数重载底层是怎么处理的?
将该程序在C++和C编译器中分别运行:
int Add(int right, int left);
int main()
{
Add(1,2);
return 0;
}
C中报错信息:
C++中报错信息:
由此错误可看出,编译器在底层使用的不是ADD函数名,而是被重新修饰过的名字,C语言中只是在函数名前加了"_",而C++编译器修饰后的名字包含了函数名及参数类型,这就是为什么C语言中不支持重载而C++中支持重载,C++对函数名进行了修饰,将参数类型包含在其中,以此来保证名字在底层的全局唯一性。
-
C++中能否将一个函数按照C风格进行编译?
在函数前加extern “C”即为将该函数按照C语言规则来编译。
extern "C" int Add(int left, int right);
int main()
{
Add(1, 2);
return 0;
}
运行该程序可得到此报错信息,证明编译器已经将该函数按照C语言的风格进行编译了
六、引用
-
概念:指给已存在的变量取一个别名,编译器不会为引用变量重新开辟内存空间,它与它引用的变量共用同一块内存空间。
-
表示:类型&引用变量名 = 引用实体; (&为引用标记符)
-
特性
-
引用在定义时必须初始化
-
一个变量可以有多个引用
-
引用一旦引用一个实体,再不能引用其他实体
-
注:引用类型必须和引用实体是同种类型的
-
#include<iostream>
using namespace std;
int main()
{
int a = 0;
int b;
char c;
int & ra = a;
int & rra = a; //correct,一个变量可以有多个引用
int &rb; //error:引用变量rb需要初始值设定项
int &ra = b; //error "ra"重定义;多次初始化
int &rc = c; //error:引用类型必须和引用实体是同种类型的
cout << "ra" << " " << "rra"
<< " " << "rb" << " " << "rrb" << endl;
return 0;
}
•常引用
const int a = 10;
//int& ra = a; error:a为常量,引用与实体类型不同
const int &ra = a;
//int &b = 10; error,b为常量
const int& b = 10;
double c = 1.314;
//int &rc =c; //error,引用与实体类型不同
const int&rc = c;//correct
注:rc引用的实体不是c,而是中间变量1(c的整数部分),通过const将rc引用实体改为新开辟空间内的1,而非double类型的c
c = 21.12;
cout << c << " " << rc << endl;//输出结果为21.12 1;c值已改动,rc值未改动,说明c不是rc的引用实体
•数组的引用
int array[10];
int(&rarray)[10] = array;
rarray[0] = 10;
cout << array[0] << " " << rarray[0] << endl;//输出结果均为10,说明rarray是array的引用,共用同一块空间
•函数参数的引用
void Swap(int &a, int &b)
{
int temp =a;
a = b;
b = temp;
}
•函数返回值的引用
#include<iostream>
using namespace std;
void Swap(int &a, int &b)
{
int temp =a;
a = b;
b = temp;
}
int& Test (int &a)
{
a += 10;
return a;
}
int main()
{
int a=10;
int b=20;
Swap(a, b);
cout<<Test(a)<<" "<<endl;
return 0;
}
//输出结果为30,证明上述函数中参数引用、返回值引用均有效
•注意不能返回栈空间上的引用
#include<iostream>
#include<stdlib.h>
using namespace std;
//int a = 1;
int& Test1(int& a)
{
a += 10;
return a;
}
int& Test2()
{
int a = 1;
a += 20;
return a;
}
int& Test3(int a)
{
(int&)a += 30;
return a;
}
int main()
{
int a = 1; a为局部 a为全局
cout << Test1(a) << " " << endl; //11 11
cout << Test1(a) << " " << endl; //21 21
cout << Test1(a) << " " << endl; //31 31
int & ret=Test2( );
cout << ret << " " << endl; //21 51
cout << ret << " " << endl; //2576716000 51
cout << Test2() << " " << endl; //21 71
cout << Test2() << " " << endl; //21 91
int&r = Test3(a);
cout << ret << " " << endl; //13243628 15406316
cout << ret << " " << endl; //13243628 15406316
cout << Test3() << " " << endl; //61 121
cout << Test3() << " " << endl; //61 121
system("pause");
return 0;
}
由上述程序可发现,将局部变量参数修改为全局变量即可解决调用结束后a值销毁无法控制其引用值的问题
-
传值、传址、传引用效率比较
struct A
{
int array[10000];
};
void TestFunc(A a)
{
a.array[0] = 0;
a.array[1] = 1;
}
void TestRef()
{
long begin = GetTickCount();
A a;
for (int i = 0; i < 1000000; ++i)
TestFunc(a);
long end = GetTickCount();
cout << "count time:" << end - begin << endl;
}
//传值 A a count time:13266
//传引用A&a count time:78
//传地址A*a count time:78
引用和指针在传参上效率几乎相同,传值占用空间大,效率低。
引用和指针的比较
-
不同点
-
引用在定义时必须初始化,指针没有要求
-
引用在初始化时引用一个实体后,就不能再引用其他实体(一对一),而指针可以在任何时候指向任何一个同类型实体
-
没有NULL引用,但有NULL指针
-
在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
-
引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小,有多级指针,但是没有多级引用
-
访问实体方式不同,指针需要显式解引用,引用通过编译器自己寻址
-
引用比指针使用起来相对更安全
-
相同点
-
底层实现方式相同,都是以指针方式来实现的
int main()
{
int a = 10;
int& ra = a;
cout << "&a = " << &a << endl;
cout << "&ra = " << &ra << endl;
return 0;
}
七、内联函数
-
概念:
-
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率
-
-
特点:
-
inline是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环/递归的函数不适宜使用作为内联函数。
-
inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联。
-
在编译器release版本下查看反汇编,或修改编译器设置值后在debug版本下查看(编译器默认不优化,因此需进行设置),可得到如下结果,增加inline关键字将其改为内联函数的是在调用位置将其展开的。
八、宏的优缺点?
-
优点:
-
增强代码的复用性。
-
提高性能
-
-
缺点:
-
不方便调试宏。(因为预编译阶段进行了替换)
-
导致代码可读性差,可维护性差,容易误用。
-
没有类型安全的检查
九、C++有哪些技术替代宏?
-
常量定义:用const替换宏
-
函数定义:用内联函数替换宏
-
类型重定义:用 typedef替换宏