函数调用,当你的**形参声明**与 调用的参数不是同一种类型,他会给你创建一个**临时变量**,用于转换。
函数传递参数的三种方式:
(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
该关键字将禁用隐式转换,必须要显示转换,如果出现隐式转换错误,就会编译错误;有利于找出问题。