【C++】(初识---缺省参数、引用、内联函数、函数重载等)

一、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++中允许同一作用域内声明多个命名相同形参列表(参数个数、类型、顺序)不同的函数,函数重载常用于处理实现功能类似数据类型不同的问题

  • 函数重载的条件:

  • 相同的作用域
    1. 全局作用域
    2. 函数内部局部作用域
    3. 命名空间作用域
    4. 类作用域
  • 函数名必须相同

  • 参数列表必须相同

  • 仅函数返回值不同,不能构成重载

几个典型函数重载的例子

//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替换宏 

猜你喜欢

转载自blog.csdn.net/lxf_style/article/details/81166011