二十、函数(三)
1、函数重载
函数重载技术运行我们创建函数名称相同的函数,但是为了编译器能够正确的编译这个程序,这些函数需要采用不同的参数列表来区分(即函数名相同,参数类型不同)。C语言中无法进行函数重载
1)项目设计
①设计一个函数,能够使得函数求出两个int值的平均值
②设计一个函数,能够使得函数求出三个int值的平均值
③设计一个函数,能够使得函数求出三个float值的平均值
//不使用函数重载实现上述功能
#include <iostream>
int Ave_1(int a, int b)
{
return (a + b) / 2;
}
int Ave_2(int a, int b, int c)
{
return (a + b + c) / 3;
}
float Ave_float(float a, float b,float c)
{
return (a + b + c) / 3;
}
int main()
{
std::cout << Ave_1(2, 3) << std::endl;
std::cout << Ave_2(2, 3, 4) << std::endl;
std::cout << Ave_float(2.2, 3.3, 4.4) << std::endl;
}
//使用函数重载实现上述功能
#include <iostream>
int Ave(int a, int b)
{
return (a + b) / 2;
}
int Ave(int a, int b, int c)
{
return (a + b + c) / 3;
}
float Ave(float a, float b, float c)
{
return (a + b + c) / 3;
}
int main()
{
std::cout << Ave(2, 3) << std::endl;
std::cout << Ave(2, 3, 4) << std::endl;
std::cout << Ave(2.2f, 3.3f, 4.4f) << std::endl;
}
2)尽管函数名称相同,但是再我们调用函数时,是通过函数参数的不同,编译器还是能够确定我们调用的是哪一个函数,因此函数能够准确的编译。但是编译器无法单纯的通过返回值确定你要返回的函数,比如
float Ave(int a,intb)和int Ave(int a,int b)是无法重载的
3)思考如下两个函数能否重载
/*不允许重载,因为数组也相当于指针
#include <iostream>
int Ave(int* pa, int count)
{
return 0;
}
int Ave(int pa[], int count)
{
return 0;
}
*/
//以下情况也不允许重载,编译不报错,但是使用函数时报错
#include <iostream>
int Ave(int& a, int& b) //传入引用参数
{
return (a + b) / 2;
}
int Ave(int a, int b)
{
return (a + b) / 3;
}
int main()
{
int a = 100;
int b = 100;
//std::cout << Ave(a, b) << std::endl; //报错
std::cout << Ave(2, 3) << std::endl; //不出错,临时的常量不存在引用,所以
}
//以下情况也不允许重载
#include <iostream>
int ave(int& a, int& b)
{
return a + b;
}
float ave(float a, float b)
{
return a * 2 + b;
}
int main()
{
char a{ 100 }, b{ 200 };
//std::cout << ave((int)a, (int)b)<< std::endl; //转化后的int类型的a和b是一个临时的变量,而临时变量没有引用,即没有固定的内存地址,所以无法转化
}
//无法重载
#include <iostream>
int ave(int a, int b) //参数为变量
{
return a + b;
}
int ave(const int a, const int b) //参数为常量
{
return a * 2 + b;
}
int main()
{
int a{ 100 }, b{ 200 }; //不管a和b是什么类型,都无法影响到函数中的值的变化,因为传递的是值
//std::cout << ave(a,b)<< std::endl; //编译报错,因为传递的是值
}
//可以重载
#include <iostream>
int ave(int& a, int& b) //参数为变量的引用
{
return a + b;
}
int ave(const int& a, const int& b) //参数为常量的引用
{
return a * 2 + b;
}
int main()
{
int a{ 100 }, b{ 200 };
std::cout << ave(a,b)<< std::endl; //实现了重载,调用的是参数为变量的引用的函数
const int c{ 100 }, d{ 200 };
std::cout << ave(c, d) << std::endl; //实现了重载,调用的是参数为常量的引用的函数
}
//不可以重载
#include <iostream>
int ave(int a=150, int b=250) //函数有默认参数
{
return a + b;
}
int ave(float a=300.0f, float b=150.0f) //函数有默认参数
{
return a * 2 + b;
}
int main()
{
//std::cout << ave() << std::endl; //编译出错,因为直接调用ave(),不带参数,无法确定到底调用哪个函数
}
2、函数模板
1)引出函数模板:统一一个函数模板将功能系相同参数不同的函数统一表示
int ave(int a,int b, int c)
{
return (a+b+c)/3;
}
float ave(float a,float b,float c)
{
return (a+b+c)/3;
}
//int ave(int a,int b,int c)和float ave(float a,float b,float c)除了类型不同外,函数的运算逻辑都一样,这样的函数可以利用函数模板技术来生成对应的函数
2)函数模板语法
//函数模板语法
template <typename type1>type1 ave(type1 a,type1 b)
{
return (a+b)/2;
}
int a=ave(1,2); //ave相当于int ave(int a,int b);
char =ave(1,2); //ave相当于char ave(char a,char b;)
//函数模板定义及用法
#include <iostream>
template <typename type1>type1
ave(type1 a, type1 b, type1 c) //定义一个函数模板type1,可支持各种类型
{
return a + b + c;
}
int main()
{
std::cout << ave(100, 200, 300) << std::endl;
std::cout << ave(100.1f, 200.2f, 300.3f) << std::endl;
std::cout << ave((char)100, (char)200, (char)300) << std::endl;
}
3)函数模板类型参数
//可以将type1当作一种类型,可以用来声明变量
#include <iostream>
template <typename type1>type1
ave(type1 a, type1 b, type1 c) //可以把type1当作一种类型
{
type1 x = a; //可以通过type1申明变量
return a + b + c + x;
}
int main()
{
std::cout << ave(100, 200, 300) << std::endl;
std::cout << ave(100.1f, 200.2f, 300.3f) << std::endl;
std::cout << ave((char)100, (char)200, (char)300) << std::endl;
}
4)指定函数模板参数
//显示的为函数模板指定一个类型
template <typename type1>type1 ave(type1 a,type1 b)
{
return (a+b)/2;
}
ave<int>(192.0f,153,1f); //相当于执行int ave(int a,int b);而不是float ave(float a,float b);
#include <iostream>
template <typename type1>type1
ave(type1 a, type1 b, type1 c)
{
type1 x = a;
return a + b + c + x;
}
int main()
{
std::cout << ave(100, 200, 300) << std::endl;
std::cout << ave<int>(100.1f, 200.2f, 300.3f) << std::endl; //强制指定函数模板参数,所有参数一律使用int
std::cout << ave((char)100, (char)200, (char)300) << std::endl;
}
5)指针在函数模板中常见的问题
#include <iostream>
template <typename type1>type1
bigger(type1 a, type1 b)
{
return a > b ? a : b;
}
int main()
{
int a{ 100 };
int b{ 200 };
int c{};
c = *bigger(&a, &b); //此处将a和b的地址传入了函数模板,比较的是a和b的地址,而不是a和b的值
std::cout << c << std::endl; //输出100,因为a的地址大于b的地址
}
3、函数模板和重载
1)函数模板的例外处理
//通过template<>定义一种函数模板的例外情况
template <typename type1>type1 ave(type1 a,type1 b)
{
return (a+b)/2;
}
template <>float ave(float a,float b)
{
return (a+b-100.0f)/2;
}
//函数模板例外处理用法
#include <iostream>
template <typename type1>type1
bigger(type1 a, type1 b)
{
return a > b ? a : b;
}
template <> //定义一种例外情况
int* bigger(int* a, int* b)
{
return *a > *b ? a : b;
}
int main()
{
int a{ 100 };
int b{ 200 };
int c{};
c = *bigger(&a, &b); //用指针来传递参数
std::cout << c << std::endl;
}
//注:此种方法只是定义了int类型指针的情况
2)函数模板和函数重载
int ave(int a,int b)
{
return a+b;
}
template <typename type1>type1 ave(type1 a,type1 b)
{
return (a+b)/2;
}
//函数重载执行顺序优先于函数模板
//函数重载执行顺序优先于函数模板
#include <iostream>
template <typename type1>type1
bigger(type1 a, type1 b)
{
return a > b ? a : b;
}
int bigger(int a, int b)
{
return a > b ? a : b;
}
float* bigger(float* a, float* b)
{
return *a > *b ? a : b;
}
int main()
{
int a{ 100 };
int b{ 200 };
int c{};
c = bigger(a, b); //用指针来传递参数
std::cout << c << std::endl;
}
3)函数模板的重载
//函数模板的重载也是通过参数来区分的
#include <iostream>
template <typename type1>type1
ave(type1 a, type1 b) //函数模板重载,2个参数
{
return a+b;
}
template <typename type1>type1
ave(type1 a, type1 b,type1 c) //函数模板重载,3个参数
{
return a+b+c;
}
int main()
{
int a{ 100 };
int b{ 200 };
int c{300};
std::cout << ave(a, b) << std::endl;
std::cout << ave(a,b,c) << std::endl;
}
4、auto->decltype
1)atuo关键字
auto可以声明一个变量,让编译器根据变量的值来推断变量的类型,例如auto a{123};相当于int a{123};
利用auto的这一特性,可以创建一个函数
auto ave(int a,int b)
{
return a+b; //根据返回值确定auto的类型
}
int ave(int a,int b)
{
return a+b;
}
//注:以上并非auto的最恰当用法,不管是函数还是变量,都不推荐使用auto来声明
2)auto关键字特性
①auto不保留const属性
const int a{};
auto b{a}; //此处的b为int类型,而不是const类型
②auto推断一个类型时,会优先推断为值类型,而非引用类型
int a{1500};
const int& la{a}; //定义一个a的引用
auto d=la; //此处d为int类型,而不是int&
③auto利用函数返回值来确定类型的时候,函数会执行
auto ave(int a,int b)
{
return a+b; //根据返回值确定auto的类型
}
auto c = ave(a,b); //c的类型依据ave()函数返回值的类型来确定
3)auto关键字和拖尾函数
//实现比较两个数,将大的数修改为500
#include <iostream>
int& bigger(int& a, int& b)
{
return a > b ? a : b;
}
int main()
{
int a{ 100 };
int b{ 200 };
bigger(a, b) = 500;
std::cout << a << '\n' << b << std::endl;
}
因为auto会优先把值匹配成值类型,而不是引用类型,所以就上述案例,如果直接将函数的返回值设置为auto类型,达不到上述案例的效果,此时可以引用拖尾函数
//拖尾函数
#include <iostream>
auto bigger(int& a, int& b)->int& //拖尾函数,通过->指定auto的类型
{
return a > b ? a : b;
}
int main()
{
int a{ 100 };
int b{ 200 };
bigger(a, b) = 500;
std::cout << a << '\n' << b << std::endl;
}
4)decltype关键字
decltype关键字可以得出一个表达式的类型
//decltype语法
语法:delctype(表达式) 声明变量; //表达式可以是
int a{100};
unsigned b{200};
decltype(a-b) x; //相当于unsigned x; 依据a-b的类型,推断出x的类型
标签:return,函数,二十,int,float,ave,type1
From: https://www.cnblogs.com/piaolaipiaoqu/p/17898132.html