Poedu_C++_Lesson02_2_20161220_重载、默认实参、inline、类型转换、引用

来源:互联网 发布:js浏览次数统计 编辑:程序博客网 时间:2024/05/16 02:57
  • 重载

    为什么要有重载

    使得我们可以方便的进行一系列的调用,重载能够实现一个功能的函数,而不用写多个。

    解决数据类型不同而引发的需要多个名字不同而功能相同的函数的问题

    重载中的数据匹配

    在实现重载机制的时候,在调用的时候有一个匹配的过程。匹配是有规则的:

    (1)精准匹配(如int->int)

    示例:


    void MyCout(int num)
    {
    }
    void MyCout(char c)
    {
    }
    int main()
    {
      int num=10;
      MyCout(num);
    }

    (2)提升匹配

    示例:

    void MyCout(int num)
    {
    }
    void MyCout(char c)
    {
    }
    int main()
    {
      long lNum=100;
      MyCout(lNum);
      short sNum=50;
      MyCout(sNum);
    }

    在重载函数中如果没有定义long型与short型的函数,当我们进行这样的调用时,会发生什么呢?如果根据规则1,并不能实现精确匹配,那么编译器会报错。事实上并没有,因为规则2:提升匹配。

    提升匹配也就是我们说的隐式转换。如:char->int float->double,由小到大的类型转换(double可以进行全匹配),由大到小会发生数据丢失。

    此时,程序可以正常运行,会通过MyCout(int num)来进行值的打印

    (3)类型转换匹配

    如:int->unsigned int

    注:反之则不行

    数据匹配的二义性

    示例


    void f1(char c)
    {}
    void f1(long l)
    {}
    int main()
    {
      int num=10;
      f1(num);
    }

    解析:因为不没有参数为int型的函数,所以不能精确匹配,又不能进行提升匹配,进行强制转换后,这两个函数又都能符合,这就造成了二义性。

    当有必要进行重载时,定义了太多或太少的函数的时候,可能会出问题,所以在进行重载的时候,根据业务需求来定。重载的时候尽量不要用int*这类的东西,很容易出错。可以先定义int,string,double这三类,需要其它的时候再加。

    出现二义性的解决办法:
    1.更改函数参数使之唯一匹配;
    2.在调用时确定精确参数:如:MyCout((int)10);

    注意

    重载会影响编译效率,不影响运行效率。

默认实参

示例1

void MyCout(int num,bool line)
{
  printf("%d",num);
  if(line)
   printf("\n");
}
void MyCout(int num,bool line=true)
{
  printf("%d",num);
  if(line)
   printf("\n");
}

代码解析:在第二个MyCout中,函数的参数有了一些小改动:bool line=true。
如果是第一个MyCout,我们在调用的时候应该是这样的:MyCout(50,true);但是,如果我们需要写多个需要换行(line=true)的函数,那么就太麻烦了,此时我们就可以用到默认实参,也就是第二个MyCout。
如果定义了第二个MyCout,那么,我们这样做就可以了:MyCout(50); 这样,编译器会默认你的ine为true。

示例2:

void MyCout(int num = 10,bool
line=true)
{
  printf("%d",num);
  if(line)
    printf("\n");
}
void MyCout(char *s="Try",bool line=true)
{
  printf("%s",s);
  if(line)
   printf("\n");
}
int main()
{
  MyCout();
}

代码解析:此时这样调用是会出错的,因为二义性。我们所写的两个函数的参数都是默认实参,这样调用就让编译器犯糊涂了,不知道应该选哪个。解决方法在上面所讲的重载中有,比如可以修改函数参数,或者MyCout(50)这样调用。

内联函数(inline)

使用示例:

inline void MyCout();

也就是在函数前加inline

优缺点

优点:

不会新建栈,只是将代码展开,由此可以大大提升运行的效率(节约了跳转的时间,分配栈的时间(可以与其它函数一起一次性分配))

缺点:

生成的体积会膨胀

代码膨胀:代码数量增加(可能增加的代码还不是自己写的),使用过多的泛型也会造成。好坏不知,但在C++中会使得调试变难。(JAVA代码出了问题可能调试不了,因为代码膨胀太厉害了,不知道代码在哪出错了)

原理

看代码:

inline void MyCout()
{
  int num=10;
}
int MyCout(char c)
{
  MyCout();
  printf();
  ...
}

如果内联成功,则会变成这样:

int MyCout(char c)
{
  //MyCout();
  int num=10;
  printf();
    ...
}

就是将内联函数(MyCout())中的内容展开到了当前调用它的函数(MyCout(char c)),使得不用跳转到MyCout(),以及分配该函数的栈空间。

注意

写了inline并不是百分百成为内联函数,会根据编译器的判断来完成,并不能强制内联。所以它只能在有限的情况下帮你完成内联,加inline是尝试帮你内联,不加则不会帮你去判断

判断标准:

(1)函数异常庞大(需要大体积的栈空间),不会变成内联(这个空间最大是多少是由编译器决定的,我们不要管)
(2)代码的调用层次复杂,这个函数会调用再调用n个内联函数,这个函数不会变成内联。
(3)不要全部都使用Inline,编译时编译器要一直判断,编译时间变长,虽然编译只要一次….

与define的区别

两者本质都是进行替换,但是有不同。define是100%会进行展开,而inline并不确定,要看编译器的判断。inline有类型的检测,它还是一个函数。而define只是进行简单的替换。

类型转换

C语言中的类型转换

示例:

int num=1.003;

隐式转换,需要给标明,告诉别人,我们是清楚这样的转换的,这是我需要的:int num=(int)1.003;

c风格,暴力,非安全,不管怎么样都会帮你转换

C++中的类型转换

(1)static_cast<>

示例:

int num = static_cast(1.003);

特性

它不是强制转换,表示转换(的过程),没法做到强制转换这件事情。对于不匹配的类型,它不会去做强制转换。

也就是说,对于int * 转换为char *这件事情,它是不会去做的。它只是告诉别人,我在这里用了转换,只起提示作用。

经常用在基类指针与派生类指针的转换。能够支持转换的用它,相对安全。用的最多的一种转换

(2)const_cast<>

特性

特定情况下使用,使得const变成非const,移除对象的量性,但是,就算移除常量性,还是不能修改内容。主要用于参数的匹配(转换),用于重载中不实现const的版本。

(3)reinterpret_cast<>

示例:

int num=10;
char *str="Try";
num=reinterpret_cast<int>str;

特性:

reinterpret_cast<>是真正意义上的强制转换,在二进制层面进行转换(有点类似memcpy()),但是能不用就不要用,是一个非常危险的操作。不安全,但还是开放,让你使用。

(4)dynamic_cast<>

类型推导转换符

引用

作用

引用能够实现和指针一样的功能——通过引用也可以找到其所绑定的空间(削弱版的),但是会比指针更加安全(指针指向的空间可以被修改,引用不可以)。

示例

int val=100;
int &refval=val;

解析

引用不能赋值为nullptr,必须指向一个变量,因为引用是没有独立空间的(指针有独立空间),它可以看作是变量的别名(refval=val),引用和变量公用一块内存,所以引用必须指向某一个变量,而且,引用一旦确定,无法再更改,引用也可以理解为削弱版的指针

运用示例

void swap(int &lhs,int &rhs)

  int temp=rhs;
  rhs=lhs;
  lhs=temp;

解析:这是一个简单的交换函数,以前我们写这个函数的时候,传入的是指针,现在改为引用,依旧可以达到交换数值的效果。
代码中的参数lhs与rhs分别代表左手边与右手边

int main()
{
  int &num=100;
  int *pNum=&num;
}

解析:代码中,先在常量区分配了一颗内存,存储100这个数值,然后将num与100进行了绑定。再将引用(也是一个地址)赋值给了一个int型的指针,那么pNum也会指向存储100的这块空间。

但是不要这样去用,一来这没有意义,因为指向的是一个常量区,其值是无法修改的。二来这也是属于未定义的行为,怎么处理要依靠编译器去判断。

按引用传递


void swap(int &rhs,int &lhs)
int main()
{
  int val=52,num=100;
  swap(val,num);
}

好处:
传入的是地址,但是与常量无异
int temp=rhs;//将参数作为变量使用.
函数间的值传递是通过栈来完成的(push),此时push的地址(是结构体时,能省空间)

引用作为返回值

int arry[5]={0,1,3,43};
int &Index(int index)
{
  return arry[index];
}

补充

(1)定义空指针用nullptr

(2)void 是C语言的产物,在C++中用重载可以实现void所做的事情

(3)关于const的一些知识

示例:

int main()
{
int num=100;
const int *pNum=&num;
int const *pNum=&num;
int *const pNum=&num;
}

解析:上述三行代码,因为有const的存在,必然是有些东西不能修改的,那么是分别是哪些呢?

const int *pNum=&num

值不能修改

int const *pNum=&num

值不能修改

int *const pNum=&num

地址不能修改

记住:const是左结合

2 0
原创粉丝点击