函数(C++版)目录


一、C++的程序组件

函数(C++版)_ide

函数调用类似于公司里的分级管理层次。

二、数学库函数

函数(C++版)_c++_02

三、函数原型和实参的强制转换

1、函数原型也称为​函数声明​

2、函数原型告诉编译器函数的​名称、函数返回值类型、函数预期接受的参数个数以及参数的类型和顺序。​

例如:

#include<iostream>
float max(float x,float y); //prototype

int main()
{
std::cout << max(3.5,4.5) << endl; //call
return 0;
}

float max(float x,float y)
{
return x > y ? x : y; //definition
}

2、函数签名:函数原型的​函数名​和​参数类型部分​称为​函数签名​或者​签名​


(1)、在同一作用域内的函数必须有独一无二的签名。
(2)、如果自同一作用域内的两个函数具有相同的签名,但是具有不同的返回类型,那么会导致编译错误。


3、基本数据类型的升级层次结构

函数(C++版)_c++_03


(1)、实参类型和虚参类型并不完全对应时,编译器会在调用函数前把实参转换为适当的类型。这种转换是遵循C++的升级规则的。
(2)、在升级层次中相对较高层次的数据类型转换成较低层次的数据类型时,会破坏数据的值,从而导致信息的丢失。


四、存储类别

1、标识符的存储类别决定了标识符在内存中存在的时间。

标识符:自动变量、寄存器变量、外部变量、静态局部变量mutable(可变的)专门与类一起使用。

2、存储类别分类

(1)、自动存储变量:在程序执行时进入到定义它们的程序块时创建,在程序块激活时存在,在程序执行退出时销毁。

(2)、静态存储类别:==全局变量在整个程序执行过程中保留他们的值,用static声明的局部变量仅在声明其所在的函数所知。

五、作用域规则

1、函数作用域:
标签------标签用在goto语句中

2、文件作用域:声明于任何函数或者类之外的标识符全局函数、函数定义、函数原型等。

3、语句块作用域:在一个语句块中声明的标识符,如:局部变量、函数参数。

4、函数原型作用域:函数原型参数列表中的标识符。

5、类作用域

下面给出一个作用域的示例:

#include<iostream>
using std::cout;
using std::endl;

void useLocal(void);
void useStaticLocal(void);
void useGlobal(void);

int x =1; //global variable

int main()
{
int x = 5; //local variable to main

cout << "local x in main's outer scope is " << x << endl;

//start new scope
{
int x = 7; //hides x in outer scope
cout << "local x in main's inner scope is " << x << endl;
} // end new scope

cout << "local x in main's outer scope is " << x << endl;

useLocal();
useStaticLocal();
useGlobal();
useLocal();
useStaticLocal();
useGlobal();

cout << "\nlocal x in main is " << x << endl;
return 0;
}

void useLocal(void)
{
int x = 25; //initialized each time useLocal is called

cout << "\nlocal x is " << x << " on entering useLocal" << endl;
x++;
cout << "local x is " << x << " on exiting useLocal" << endl;
}

void useStaticLocal(void)
{
static int x = 50; //initialized first time useStaticLocal is called

cout << "\nlocal static x is " << x << " on entering useStaticLocal" << endl;
x++;
cout << "local static x is " << x << " on exiting useStaticLocal" << endl;
}

void useGlobal(void)
{
cout << "\nglobal x is " << x << " on entering useGlobal"<< endl;
x*=10;
cout << "global x is " << x << " on exiting useGlobal" << endl;
}

测试示例

函数(C++版)_c++_04

六、无参函数

C++中空的函数参数列表的含义与C语言中的有很大不同。在C中,空的参数列表意味着不进行任何参数检查(即函数调用可以传递它想传递的任何参数);在C++中,空的参数列表意味着函数明确的不需要任何参数。因此,使用该特性的C程序在C++中编译可能会引起编译错误。

七、内联函数

int main(int v1,int v2)
{
return (v1 < v2 ? v1 : v2);
}

对以上代码进行分析

1、调用函数比直接进行条件判断要慢

(1)、要传递两个参数。
(2)、程序要定位到新的地址。

2、​C++提供内联函数来减少程序调用的开销------特别是对于小程序。​


指定为内联函数在被调用的程序中展开。例如:
int minVal2 = min(i,j);
编译时被展开为:
int minVal2 = i < j ? i : j;
(消除了调用函数时的运行时开销)


下面是一个计算立方体体积的inline函数

#include<iostream>
using std::cout;
using std::cin;
using std::endl;

inline double cube(const double side)
{
return side * side * side;
}

int main()
{
double sideValue;

cout << "Enter the side length of your cube: ";
cin >> sideValue;

cout << "Volume of cube with side "
<< sideValue << " is " << cube(sideValue) << endl;

return 0;
}

运行结果:

函数(C++版)_作用域_05

3、在函数定义中把限定词inline放在函数的返回类型的前面会“建议”编译器在适当的地方生成函数代码的副本以避免函数调用。编译器可以忽略inline限定词,并且对于除了小函数外的函数,通常会这样做。

4、可重用的内联函数一般都放在头文件中,以便使它们的每个源文件都能包含它们的定义。

5、inline限定词应该​只是适用于小的、经常使用的函数​。

6、对内联函数所作的任何修改要求该函数所有客户进行重新编译。这在程序开发和维护中是非常重要的。

7、使用内联函数可以减少执行时间,但是会增加程序尺寸。

八、引用和引用参数

1、按值传递的一个缺点是:如果传递了一个大数据项,复制该数据可能需要大量的执行时间和内存空间。

2、引用参数

(1)、引用有时也被称为​别名​,作为对象的​另一个名字​来使用。

(2)、​引用允许像指针一样对对象直接操作​,但不需要使用指针语法。

3、引用的定义

(1)、引用定义在数据类型之后加&标识即可。​引用必须初始化​。

int ival = 1024;
int &refVal = ival; //正确引用
int &refVal2; //错误引用,没有初始化

(2)、一旦定义,引用不允许​再指向其它对象​

(3)、​作用在引用上的所有操作实际应用于被引用的对象。​

int count = 1;
int &cRef = count; //声明引用
cRef++; //实际是count++

按值传递和按引用传递参数的示例

#include<iostream>
using std::cout;
using std::endl;

int squareByValue(int); //按值传递
int squareByReference(int &); //按引用传递

int main()
{
int x = 2;
int z = 4;

cout << "x = " << x << " before squareByValue\n";
cout << "Value returned by squareByValue: "
<< squareByValue(x) << endl;
cout << "x = " << x << " after squareByValue " << endl;

cout << "z = " << z << " before squareByReference " << endl;
squareByReference(z);
cout << "z = " << z << " after squareByReference " << endl;

return 0;
}

int squareByValue(int number)
{
return number *= number;
}

int squareByReference(int &Refnumber)
{
return Refnumber *= Refnumber;
}

运行示例

函数(C++版)_c++_06

(4)、按引用传递对性能很有帮助,因为它可以消除按值传递复制大量数据的开销;​按引用传递可能会削弱安全性,因为被调用函数可能破坏调用者的数据​。

(5)、​在声明引用变量时不初始化它会产生编译错误,除非此声明是函数参数列表的一部分。当引用参数声明所在的函数被调用时它们被初始化​。

(6)、​函数可以返回引用,但是被调用函数中的变量必须声明为static,否则函数结束时变量被销毁。​

从函数返回引用

#include<iostream>
using namespace std;

int &func1(int x) //函数返回引用
{
static int sum = 0;
x++;
sum = sum + x;
return sum;
}

int main()
{
int a;
a = 10;
cout << "func1()= " << func1(a) << endl;
return 0;
}

九、默认参数

1、当重复调用函数时使用相同的参数,程序员可以对这样的实参指定默认值。

int boxVolume(int length = 1,int width = 1.int height = 1);

2、当一个程序在函数调用中对默认形参省略了其对应的实参时,编译器重写这个函数调用,并且插入那个实参的默认值作为函数调用所传递的实参。

int add(int x,int y = 10);
add(15);------>add(15,10);

3、​默认实参必须是函数参数列表中靠右边的参数​

void f(int x,int y = 1,int z);      //错误

4、默认实参在函数第一次出现时指定------通常在函数原型中。

5、在函数原型和函数头部中同时指定默认参数会产生编译错误。

6、使用默认参数可以简化函数调用的书写。然而,有些程序员觉得显示地指定所有的实参更加清晰。
7、如果函数的默认值改变了,所有使用该函数的客户代码必须重新编译。

十、一元作用域分辨运算符

一元作用域运算符(::)能够访问全局变量,不能访问局部变量。例如:

#include<iostream>
using std::cout;
using std::endl;

int number = 7; //全局变量

int main()
{
double number = 10.5; //局部变量
cout << "Local double value of number = " << number
<< "\nGlobal int value of number = " << ::number <<endl;

return 0;
}

运行示例

函数(C++版)_作用域_07

十一、函数重载

1、​C++允许定义多个具有相同名字的函数,只要这些函数的参数不同(至少参数类型、或者参数数目或者参数类型的顺序不同)。这种特性称为函数重载。​

2、当调用一个重载函数时,C++编译器通过检查函数调用中的实参的数目、类型和顺序来选择恰当的函数。

3、函数重载通常用于创建执行相似任务、但是作用于不同的数据类型的具有相同名字的多个函数。

4、重载执行紧密相关任务的函数可以使程序更易阅读和理解。

示例:

#include<iostream>
using std::cout;
using std::endl;

int square(int x)
{
cout << "square of integer " << x << "is ";
return x * x;
}

double square(double y)
{
cout << "square of double " << y << "is ";
return y * y;
}

int main()
{
cout << square(7);
cout << endl;

cout << square(7.5);
cout << endl;

return 0;
}

运行结果:

函数(C++版)_ide_08

5、创建​具有相同参数列表和不同返回类型的重载函数​会产生编译错误。

十二、函数模板

使用函数重载进行两个不同类型数据的相加

//int 类型
int add(int a,int b)
{
return a + b;
}

//double 类型
double add(double a,double b)
{
return a + b;
}

//可否参数化?
T add(T a,T b)
{
return a + b;
}

(1)、什么是函数模板?

template<class Type>
Type min(Type a,Type b)
{
return a < b ? a : b;
}

int main()
{
min(10,20); //ok,int min(int,int);
min(10.0,20.0); //ok,double min(double,double);
return 0;
}

(2)、如何定义函数模板?

以关键字template开头定义声明一个函数模板。
模板参数列表中的参数由关键字typename或class开头。

template <class T> //模板参数列表
T abs(T val)
{
return val < 0 ? -val :val;
}

(3)、带多个类型参数的函数模板

template <class T1,class T2,class T3>
template <class T1, T2, T3> //错误

//例如:
template <class T1,class T2>
{
return x >= y ? x : (T1)y;
}

模板实例化

函数模板指定了给定一组或多个实际类型或值时,如何构造单个函数。这个构造过程称为模拟实例化。

当调用函数模板时,模板参数的类型和值是通过检查函数实参的类型来确定的。