c++之深入了解函数

函数调用,当你的**形参声明**与 调用的参数不是同一种类型,他会给你创建一个**临时变量**,用于转换。

函数传递参数的三种方式:
(1)按值传递; 应用场景:内置类型
(2)指针传递; 结构体,特别是数组只能用指针,或者需要更改的内置类型
(3)引用传递; 类,结构体,或者需要更改的内置类型
不更改,加入const,更改则不加

1、函数与const

const 修饰一个常量(只读),相当于是个权限,不能被赋值。
c++允许非const 指针到const指针;反之,则需要强制转换。
eg:const指针

   #include <QCoreApplication>
#include <QDebug>
int sum_arr(const int *arr, int n);
const int arSize=5;
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    int cookies[arSize]={1,2,4,8,16};
    int sum= sum_arr(cookies,4);
    qDebug()<<"地址"<<&cookies<<cookies[0];
    qDebug()<<sum;

    int sloth=3;
    int a1=20;
    const int *ps=&sloth; //常量整型 指针
    int *const p=&sloth;//整型的 常量指针
//第一个不能改变值,第二个不能改变地址

    ps=&a1;
//  *ps=90;错误的
    qDebug()<<*ps;
//  p=&a1; //错误
    *p=200;
    qDebug()<<sloth;

    return a.exec();
}
//函数传递数据,传递的是指针,arr*=arr[],*(arr+i)=arr[i];
int sum_arr(const int arr[], int n)
{
    qDebug()<<arr;
    int total=0;
    for (int i=0;i<n;i++)
        total=total+arr[i];
    return total;
}

eg:传递结构体的两种方式

1、值传递
struct testStr
{
  public:
   double a;
   double a1;
};
testStr add(testStr,testStr);
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    testStr t1={2,3};
    testStr t2={3,4};
    testStr sum= add(t1,t2);
    qDebug()<<sum.a<<"sx"<<sum.a1;
    return a.exec();
}
testStr add(testStr tmp1,testStr tmp2)
{
  testStr total;
  total.a=tmp1.a*2+tmp2.a*3;
  total.a1=tmp2.a1+tmp1.a1;
return total;
}
2、传递地址
testStr add(testStr *,testStr*);
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    testStr t1={2,3};
    testStr t2={3,4};
    testStr sum= add(&t1,&t2);
    qDebug()<<sum.a<<"sx"<<sum.a1<<t1.a;
    return a.exec();
}
testStr add(testStr *tmp1,testStr *tmp2)
{
  testStr total;
  tmp1->a=5;
  total.a=tmp1->a*2+tmp2->a*3;
  total.a1=tmp2->a1+tmp1->a1;
return total;
}
//避免 被修改加 const限定符
2、函数指针

作用:方便被调用,速度优于直接调用。
作用参考博客:https://blog.csdn.net/wujiangguizhen/article/details/17153495

double Volume(int a);
double (*p)(int);
void estimate(int a,double (*p)(int));
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    estimate(4,Volume);

    return a.exec();
}
double Volume(int a)
{
    double s=a*3;
    return s;
}
void estimate(int a, double (*p)(int))
{
    qDebug()<<a;
    qDebug()<< p(a); //与下面那个等价
    qDebug()<<(*p)(a);//建议用这种方式,方便区分
}

auto不能用于初始化列表。

3、内联函数

https://www.cnblogs.com/ht-927/p/4726570.html
在这里插入图片描述
宏的概念
在这里插入图片描述
在这里插入图片描述
eg 如下:

代码:
#include <QCoreApplication>
#include <QDebug>
#include <iostream>
inline double square(double x){return x*x;}
#define squa(x) x*x 
/*宏的意义是 字符替换,(a->a^2);
缺点:容易造成代码膨胀(代码过多),存在二义性等
优点:不占用执行时间,占用编译时间。
使用:宏定义参数需要 加上括号,c++,会使参数被加两次,解决方案,加一步临临时变量即可。
#define squa(x) (x)*(x) */

/*内联的作用:
执行时,把代码复制到调用的部分,减少了寻址时间(寻找函数地址以及返回地址的时间)
 //函数的调用是需要付出一定的时空开销的,因为系统在调用函数时,要保留现场,然后转入被调用函数去执行,调用完,再返回主调函数,此时再恢复现场,这些操作//

缺点:需要程序十分简单,不能带有复杂的结构;如果行数过多也不适合做成内联;也会产生临时变量,以值传递。
优点:速度优于函数调用
*/

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    double a1,b,c=13;
    a1=square(5.0);
    b=square(4.5+7.5);

    qDebug()<<"a="<<a1<<",b="<<b;
    b=square((++c));
 //这样就是 先c+1,然后在计算的square,对比 c++,则是先调用square,在自加1
    qDebug()<<"c="<<c<<",b="<<b;
    qDebug ()<<"x="<<squa(++c)<<c;
    return a.exec();
}
3、引用变量

引用基本用于结构体、类的类型
https://www.cnblogs.com/wkfvawl/p/10536424.html

引用+const 更安全,if 不更改原始数据的值的情况下

#include <QCoreApplication>
#include <QDebug>
#include <iostream>
struct free_str{
    QString name="i love you";
    int a=0;
    int b=100;
    double c;
};
free_str foundList(free_str &c);
free_str &foundst(free_str &c);
void foundit(free_str &a,free_str &b);
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QList<int> b;
    for (int i = 0; i < 10; ++i) {
        b.append(i);
    }
    free_str dmp={"wo qu ni ma mp de ai",100,200,100.0};
    free_str dump1,dump2;//没有初始化

//    dump2=foundList(dmp);
    dump1=foundst(dmp);
    dmp.a=10;
    qDebug()<<dump1.a<<"addr"<<&dump1;
    qDebug()<<dmp.a<<"addr"<<&dmp;


    foundit(dump2,dmp);
    qDebug()<<dump2.a;
    return a.exec();
}
free_str foundList(free_str &c)//AA
//赋值给另外一个结构体,彼此独立
{
  qDebug()<<c.name;
  double bm=98.128239234;
  int tmp=static_cast<int>(bm*1000);
  c.c=static_cast<float_t>(tmp)/1000;
  return c;
}
//减少了创建临时变量的时间;直接赋值。//但是并不是 指向c的,不共用一块地址
free_str& foundst(free_str &c)//BB
{
  c.a=1000;
  qDebug()<<"c"<<"addr"<<&c;
  return c;
}
void foundit(free_str &a, free_str &b)//CC
{
  a.a=b.c;
  a.name=b.name;
  a.b=10000;
}

应用场景1:需要返回一个与原始内存一样的别名,用CC,即同一块地址
应用场景2:需要返回一个拷贝值,并且需要节约时间,用BB。数据一样,地址不同
应用场景3:CC和指针可以互换(在不涉及到多态等)

const+引用,当调用类型与 形参类型不一致的时候,就会强制转型调用参数为 形参。
如果不加const 编译器就会报错或警告。
eg:

 int c=100;test(c);
 void test(const double &a)
{   qDebug()<<a;//成功?}
4、函数默认值
//eg:函数原型
char* left(const char *str,int n=1);
//此时默认n=1,如果赋值n=3,则会覆盖n的初始值1

注意格式:必须从右往左添加默认值   
5、函数重载

c++允许定义相同名称的函数,(前提条件,特征标不同)。
特征标: 函数的数目和类型以及排列顺序相同,特征标相同 (它不在乎
const 与非const,引用或者为引用)

函数返回类型不一致,###### 参数一致不算重载

比如:

//普通
void cube(double x);
void cube(const double x);//无效

void cube(double &x);
//有效但是报错,重载函数的调用不明确
//普通

//引用
void cube(double &x);
void cube(const double &x1);//有效
void cube(double &&x);//有效 采用谁最匹配调用谁
//

应用场景:仅当函数基本上执行相同任务时,数据形式不同时,调用

6、函数模板
void swapt(float &a,float &b);//非模板函数
template <typename anytype> void swapt(anytype &a,anytype &b);//模板函数
struct job{
  char name[40];
  double salary;
  int floor;
};
template<> void swapt<job>(job &a,job &b);//模板函数  显示具体化
//编译器选择原型 非模板>模板显具>模板
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    double x1=10,x2=200;
    job c={"i o u",22.0,100};job b={"1234",99,10};
    swapt(x1,x2);
    swapt(c,b);
    qDebug()<<c.floor;
    return a.exec();
}
template <typename anytype>
void swapt(anytype &a,anytype &b)
{
    anytype w;
    w=a;
    a=b;
    b=w;
}
template<> void swapt<job>(job &a,job &b)
{
   double t1;
   int t2;
   t1=a.salary;
   a.salary=b.salary;
   b.salary=t1;
   t2=a.floor;
   a.floor=b.floor;
   b.floor=t1;
}

函数模板具现化应用场景 :
当模板的通用的函数实现不了 目标时, 需要新增一个函数 来实现,具现化即可(新建一个函数也行)

具体化 包括 隐式实例化、显示实例化、显示具体化

隐式实例化; 编译器自动根据形参参数不同 实例化的函数
显示实例化; 手动实例化的模板函数
显示具体化; 手动创建的不同模板函数方法的函数

eg: template <typename T>
    void Swap(T&,T&); //这是一个模板函数
    
   //调用
    int a,b;
    Swap(a,b);//隐式实例化
    template void Swap<float>(float &,float&);
    //显示实例化
struct job{
  char name[40];
  double salary;
  int floor;
};
template<> void swapt<job>(job &a,job &b);
//模板函数 显示具体化   
7、decltype 和auto

decltype 为类型判断和 赋值类型

8、关键字explicit

该关键字将禁用隐式转换,必须要显示转换,如果出现隐式转换错误,就会编译错误;有利于找出问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值