形参和实参
实参是形参的初始值
局部对象
对象有生命周期,理解这两个概念非常重要
名字的作用域是程序文本的一部分,名字在其中可见
对象的生命周期是程序执行过程中该对象存在的一段时间
局部静态对象
相对于自动对象而言,局部静态对象在程序执行路径第一个经过对象定义语句时初始化,直到程序终止了才会被销毁,在此期间机制对象所在的函数执行结束也不会有影响。
/*
1.验证局部变量对全局变量的隐蔽性。
结果:局部变量和全局变量同名时,局部变量对全局变量具有隐蔽性。
2.验证局部静态变量的生命周期
结果:局部静态变量的生命周期是在整个程序运行期间都存在
需要注意的是静态声明的关键字是static ,而不是const
static 声明对象,会使得对象储存在内存四区的全局静态区
const 声明仅仅只是该该对象内容不可被改变
*/
#include <iostream>
#include <string>
using namespace::std;
int value_test = 5;
void object_test()
{
int value_test = 0; //与全局变量的名字相同
static int value_static_test = 0;
value_static_test++;
cout << "value_test:" << value_test << endl;
cout << "value_static_test:" << value_static_test << endl;
}
int main(void)
{
for (int i = 0; i < 4; i++)
{
object_test();
}
system("pause");
return 0;
}
尽量使用常量引用
把函数不会改变的形参定义成(普通的)引用是一种比较常见的错误,这么做带给函数的调用者一种误导,即函数可以修改它的实参的值
数组做函数参数会退化成指针
eg void test(int value[]){};
void test (int *value ) {};
//这两句是是等价的,如果看客有疑惑。
可以用下例测试
int test[5] ={0,1,2,3,4};
//value :数组名 , size :数组的大小,调用时填入test和5或者sizeof(test)
//注意千万不要耍小聪明,将数组传入形参后,在函数中对形参进行sizeof()操作,
得到的并不是数组的大小,而是指针本身的大小。如有疑问,请看标题,它解释的很清楚
void test (int *value, int size)
{
for(int i = 0; i < size; i++)
cout << value[i] << endl;
}
函数中多维的数组的指针形式形参
如 int (* test)[][5] //定义一个指向一维为5,二维未知的数组指针
initializer_list形参
是一种模板类型,较为特殊的是,它对象的元素永远是常量
如果函数的参数是 initializer_list形参, 它的参数传递,只能是花括号包裹起来的列表初始化
#include <iostream>
#include <string>
#include<vector>
using namespace::std;
int value_test = 5;
void msg_cout(initializer_list<string> il)
{
for (auto beg = il.begin(); beg != il.end(); ++beg)
cout << *beg << " ";
cout << endl;
}
int main(void)
{
initializer_list<string> i2 = { "1","2" };
vector<string> i3 = { "0","1" };
msg_cout({ "ok","I am fan","thanks" });//正确,列表传参
//msg_cout(i3);//错误,vector对象不可给initializer_list对象传参
system("pause");
return 0;
}
函数重载
如果同一个作用域内的几个函数名字相同但形参列表不停,称之为重载函数
eg : void print(const char *p)
void print(const int *beg, const int *end)
int j[2] = {0,1};
print("hello");//调用第一个
print(begin(j), end(j));//调用第二个
/*
1.使用函数返回引用
结果:返回值可以做左值
2.使用函数返回列表
3.返回数组指针
4.函数重载是否能靠const性质分辨
结果:如果形参本身是普通对象,会显示函数重复声明
如果形参是指针或者引用类型,它会做以区分(仅在底层声明有效)
原因:函数的形参是传值调用,
普通形参,接收的实参过程仅仅是复制其实参对象内容,对象内容无法区分cosnt与非const
eg:实参 string s1 = "a", s2 = "b";
函数test(string s);
test(s1); 实际上做的操作仅仅是 s = s1;再直白点就是 s = "a";
那么test(s2) 与上述过程相同,无法靠这个过程去区分实参是否是const,并且也无必要
而引用和指针传入时,带有其本身的性质,底层const声明表示其指向对象是不能被改变的
因而可以区分
注意:如果传入的实参与重载函数的形参类型都没有符合的,它会选择最佳匹配,并生成调用该函数的代码
但是以下情况会报错
1.找不到能够匹配的函数就会发出无匹配的错误信息
2.有多于一个函数可以匹配,但是都不是最佳匹配,也报错,成称为二义性调用
最佳匹配条件
1.该函数的每个实参的匹配都不劣于其他可行函数需要的匹配
2.至少有一个实参的匹配优于其他可行函数提供的匹配
*/
#include <iostream>
#include <string>
#include<vector>
using namespace::std;
char &back_char(string &str, string::size_type ix)
{
return str[ix];
}
vector<string> back_string(void)
{
return{"a","b","c"};
}
void test_const(const int &i)
{
cout << "const" << endl;
}
void test_const(int &i)
{
cout << "not const" << endl;
}
int (*arr())[5] //表示解引用arr的调用会一个大小为5的数组
{ //或是说返回值是一个指向大小为5的数组的指针
int (*p)[5] = NULL;
p = (int(*)[5])malloc(sizeof(int) * 5);
return p;
}
int main(void)
{
string s("a value");
vector<string> s1(3);
int a = 0;
const int b = 0;
cout << "s_f:" << s << endl;
back_char(s, 0) = 'A';
cout << "s_d:" << s << endl;
s1 = back_string();
cout << "s1[0]:" << s1[0] << endl;
cout << "s1[1]:" << s1[1] << endl;
cout << "s1[2]:" << s1[2] << endl;
test_const(a);
system("pause");
return 0;
}
默认实参
某些函数有这样一种形参,再函数的很多次调用中他们都被赋予一个相同的值,这时,我们把这个反复出现的值称为函数的默认实参
eg :void test(int a = 1; int b = 2){}
以上的1,2便是默认实参,如果没有给其传入参数其就使用默认实参初始化形参
想要使用默认实参时,省略即可
eg test(1); test();均可
需要注意的是;仅仅只能省略尾部的实参 如果test(,1);这样用时错误的。
内联函数inline
将函数指定为内联函数,通常就是将它再每个调用点“内联的”展开。在函数前加上inline即可指定。
eg inline int test(){ return 0;}
在编译过程中,test()会被展开为return 0;从而消除了test()韩式运行的开销
注意:一般这个函数使用次数较多时才声明为inline。因而最好定义放在头文件,以供多次调用。
尾置返回类型
该类型提供,一种更好理解的返回类型书写方法
格式eg:auto f1(int)-> int(*)[];//返回类型为一个指向int型数组的指针
多提一句
函数指针的东西
using f = int(int*,int);//f是函数类型,并不是指针
using pf = int(*)(int*,int);//pf是函数指针