函数:
C++ 程序都至少有一个函数,即主函数 main()
主函数的位置:任意位置
函数也称 方法
格式:
返回值类型 函数名(形参)
{
内容;
}形参可以0个和多个
无返回值 void一般来形参不可以修改实参
void text()
{
cout << "pp" << endl;
}
int text2(int a)
{
return a;
}
函数声明:函数声明会告诉编译器函数名称及如何调用函数
格式: 返回值类型 函数名(形参);
void text();//让编译器提前知道text()函数
int main()
{
text();
system("pause");
return 0;
}
void text()
{
cout << "pp" << endl;
}
注意事项: 在C中如果没有返回值类型,默认为int,c++中没有返回值类型会报错。
函数参数:
值传递 | 修改函数内的形式参数对实际参数没有影响。 |
引用传递 | 把实参地址给形参,使得形参可以修改实参 |
指针传递 | 参数的引用复制给形式参数,使得形参可以修改实参 |
引用:相当于起别名 类型一定要一致
引用的本质:指针 是指针常量 (例如:int * const a)
- 但引用并不会指向地址,而是存储一个地址(获取) 就是个别名
- 指针是指向内存地址
- 格式: &新名=旧名
- 使用时一定要初始化 不能为空
- 别名不可以改变指向 但别名的值可以赋值给其他变量
- const可以修饰引用,变为常引用
- 引用不能被赋值(易错点)
- 引用不能用于数组 例如: int &a[5];//出错
因为数组中的每一个元素都有一个地址,引用数组的话,就相当于给数组中每个元素引用,没有分配空间。
基本用法:
int a = 10;
int &b = a;//必须初始化,且类型要相同
//改变b 可以改变a
b = 20;//可以改变值,因为a,b指向同一块地址 b改变 a也改变
int a = 10;
int c = 10;
int &b = a;
//但可以赋值给别人 把b的值赋给c
c = b;
//不可以被赋值 会报错(不能改变指向)
&b = c;
&b = 10;
做函数参数:
void swap(int &a, int&b)
{
int p = a;
a = b;
b = p;
}
int main()
{
int a = 10;
int b = 20;
cout << "原a:" << a << " " << "原b:" <<b<< endl;
swap(a, b);
cout << "现a:" << a << " " << "现b:" <<b<< endl;
return 0;
}
引用做返回值:
- 不能返回局部变量
- 返回值可以做左值
int& text()
{
//局部变量
int a= 10;
return a;//引用做返回值时不能返回局部变量
}
//接受局部变量
int &b = text();
cout << "b的值:" << b << endl;//编译器保留了一次值 输出正确
cout << "b的值:" << b << endl;//输出错误
int a = 10;
int& text2()
{
return a;
}
//可以作为左值
text()=100;
cout << text() << endl;
常量引用:
主要用来修饰形参:防止形参修改实参
void text(const int &a, const int &b)
{
int p = a;
//会发生报错 因为a和b不可以被改变
a = b;
b = p;
}
const int&b = 10;//相当于 int ppp=10; const int &b=10;ppp是系统生成的临时变量
指针传递:
void swap(int *a, int*b)
{
int p = *a;
*a = *b;
*b = p;
}
int main()
{
int a = 10;
int b = 20;
cout << "原a:" << a << " " << "原b:" <<b<< endl;
swap(a, b);
cout << "现a:" << a << " " << "现b:" <<b<< endl;
return 0;
}
注意事项:
void swap(int *a, int *b)
{
int *temp;
temp= a;
a = b;
b = temp;
}
int a = 10;
int b = 20;
swap(&a, &b);
//swap函数只交换了形参的地址,没有改变实参
重点:引用和指针的区别
- 引用是别名,指针是地址
- 引用定义时必须初始化,指针可以不用
- 引用的大小为引用对象的大小,指针大小为固定的与位数有关,32位 4个字节 64位 8个字节
- 引用不可以为NULL,指针可以
- 引用不存在多级,指针存在 char **a,char ***a
左值引用和右值引用:
左值引用:(应引用)
- 一般引用的初始值必须为左值
- 常量引用的初始值左右值都可以
- 函数传值时也要遵循改规则
int a = 10;
int &b = a;
int &c = 10;//报错,必须为左值
const int &d = 10;//常量引用初始化右值
const int &e = a;//常量引用初始化左值
void text(int &a)
{
cout << a << endl;
}
int a = 10;
text(a);
text(10);//错误必须为左值
void text(const int &a)
{
cout << a << endl;
}
int a = 10;
text(a);//可以
text(10);//可以
c++11中新添加了右值引用:
- 右值引用:用 "&&" 表示
- 右值引用也必须立即进行初始化操作,且只能使用右值进行初始化
- 右值引用可以对右值进行修改
- 一般不会定义常量右值引用(无实际意义)
右值引用的作用:
- 移动语意:移动语意(std::move),可以将左值转化为右值引用
- 完美转发:td::forward(arg) 将 arg 转化为 T&&
- 只有在需要的时候,才调用复制构造函数
- 左值被转发为左值,右值被转发为右值
引用折叠:
- T& & => T&
- T& && => T&
- T&& & => T&
- T&& && => T&&
int a = 10;
int &&b = a;//错误需要用右值初始化
int &&c = 10;
c = 100;//右值可以修改
//移动语义
int a = 10;
int &&b = move(a);//把左值转化为右值
函数的参数类型:
- 普通参数
- 默认参数
- 占位参数
普通参数:
//普通参数
int text(int a,int b)
{
return a + b;
}
int text(int &a,int &b)
{
return a + b;
}
int text(int *a,int *b)
{
return a + b;
}
默认参数:
- 函数有默认参数,调用时可以不给值,给了值就是用自己给出的值
- 如果你没给默认参数值,那么将使用默认参数的值
- 第一个默认参数后应该都为默认参数
- 给出的参数一定要连续
int text(int a,int b=20)
{
return a + b;
}
text(10);//结果为30
text(10, 30);//结果为40
//第一个默认参数后面全为默认参数
int text(int a,int b=20,int c)//报错
int text(int a,int b=20,int c=30)//正确
{
cout<< a + b + c<<endl;
}
text(1);
text(1,2);
text(1, 2, 3);
text(1, , 3);//错误 参数一定要连续
占位参数:
- 占位参数只有参数类型声明,而没有参数名声明
- 一般情况下,在函数体内部无法使用占位参数
- 但调用时必须填补这个参数
int text(int a,int )
{
return a ;
}
text(10,1);//必须填补
内置函数:
头文件:#include<cmath>
cos(x) | 余弦 |
sin(x) | 正弦 |
tan(x) | 正切 |
log(x) | 返回自然对数 |
pow(x,y) | x的y次方 |
hypot(x,y) | 返回两个参数的平方根总和的平方根 |
sqrt(x) | 平方根 |
abs(x)(整形) | 绝对值 |
fabs(x)(浮点型) | 绝对值 |
floor(x) | 向下取整 |
math.h的abs返回值无法确定:
因为负数的范围比正数大一个,比如8位的二进制,可以表示范围为-128~127
所以abs(-128)可能并不能表示为128,所以只能返回原值
cmath中的abs表示正常:
C++ 随机数:
生成的随机数并不是真正的随机数,也是一种算法。
srand()生成随机数种子 ,一般用time()生成
srand((unsigned )time(NULL))
rand(),只会生成伪随机数,使用前先调用srand()
rand()限制生成数:
- 先用%限制
- 再从后面加 n
- 生成[m,n] 的限制为 rand()%(n-m+1)+m
#include<iostream>
#include<ctime>
using namespace std;
int main()
{
srand((unsigned)time(NULL));
int b = rand() % 100 + 1;
cout << b << endl;
system("pause");
return 0;
}
以下为C++特有:
函数的重载:
C语言不支持函数重载,c++支持函数重载,两者在编译阶段对函数处理的方式不同
- C语言 : 在编译阶段把函数名前加上“_“修饰符,这样无法区分同名函数
- C++ : C++支持重载,使得重载函数在符号表中生成的函数名称不一样
可以在函数前加 extern "C" 可以使C++按照C语言的方式来编译文件
前提条件:函数名相同
- 参数个数不同
- 参数顺序不同
- 参数类型不同
返回值不同不算函数重载
形参内的const不算重载
void text(){}
void text(int a){}//个数不同
void text(double a){}//类型不同
void text(int a,double b){}//顺序不同
void text(double a,int a){}
//返回值不同不算重载
void text(){}
int text(){return 0}
//形参const修饰不算重载
int text(int a)
{return 0;}
int text(const int a)
{return a;}
内联函数:
C++ 内联函数是通常与类一起使用,内联函数会在编译的时候把该函数的代码副本放置在每个调用该函数的地方,相对于普通函数效率更高。(以空间代价换时间效率)
再类外:要用inline来定义函数,编译器会忽略函数声明处的 inline 关键字(可加可不加)
在类内:在类中定义的成员函数全部默认为内联函数。 可以显示加上 inline 标识符。 或者不加 在类中声明的成员函数,如果没加inline。 则在类外定义该成员函数时加了inline,该成员函数也为内联函数。
- 内联函数的内容尽可能的短,不然会影响效率,尽可能为一行
#include<iostream>
using namespace std;
inline void text(int c)//定义处添加 inline 关键词
{
cout << "2" << endl;
}
int main()
{
int b = 5;
text(b);
return 0;
}
c++11开始有匿名函数:
Lambda 函数与表达式
lambda表达式的作用:方便快捷地创建一个“函数对象“
lambda表达式的格式:
[捕获列表] (参数) ->返回类型 {函数体};
- 返回类型可以省略(自动类型推导)
- 指名返回类型
//指明返回类型
auto P=[](int a,int b)->int {return a+b};
//自动类型推导
auto P=[](int a,int b){return a+b};
在Lambda表达式内可以访问当前作用域的变量,这是Lambda表达式的闭包(Closure)行为
- []:默认不捕获任何变量;
- [=]:任何被使用到的外部变量都隐式地以传值方式加以引用
- [&]: 任何被使用到的外部变量都隐式地以引用方式加以引用
- [x]:仅以值捕获x,其它变量不捕获;
- [&x]:仅以引用捕获x,其它变量不捕获;
- [=, &x]:默认以值捕获所有变量,但是x是例外,通过引用捕获;
- [&, x]:默认以引用捕获所有变量,但是x是例外,通过值捕获;
- [this]:通过引用捕获当前对象(其实是复制指针);
- [*this]:通过传值方式捕获当前对象;
lambda表达式的用法注意事项:
- 对于[=]或[&]的形式,lambda 表达式可以直接使用 this 指针。但是,对于[]的形式,如果要使用 this 指针,必须显式传入
- 当值捕获时,想要改变值,需要使用mutable来修改,对于引用捕获的话可以直接修改
- lambda表达式是不能被赋值的
int a=10;
auto int b=[a](int a){return a*10};//不能直接修改,a为临时值 const属性
auto int b=[a](int a)mutable{return a*10};//直接修改,mutable可以修改const属性值
auto int b=[&a](int a){return a*10};//直接修改,a为数值的引用
参考文献: