第六章 函数

etherovo / 2023-05-03 / 原文

C++11

  • initialize_list -- 6.2.6

6.1 函数基础

  • 函数调用的两项工作
    • 控制权:主调函数中断,被调函数执行
    • 隐式定义并初始化形参
  • return的两项工作
    • 返回值
    • 控制权: 转移到主调函数
  • 实参的数目和类型与形参对应,如果不对应需要能够隐式转化
  • 形参列表不能忽略,如果没有形参可以为空或者void
  • 每个形参必须带有自己的类型
  • 形参不能重名,也不能与函数最外层的局部变量重名
  • 返回类型不能是数组或者函数,可以是其指针

6.1.1 局部对象

名字有作用域,对象有生命周期

  • 自动对象:控制路径经过变量定义语句时创建对象,到达所在块的末尾时销毁
  • 局部静态对象:控制路径经过变量定义语句时创建对象,程序结束时销毁

6.1.2 函数声明

  • 函数声明/函数原型:不需要写函数体,使用分号代替
  • 应该在头文件中声明,源文件中定义,然后原文件包含头文件,其他使用函数的原文件或头文件也只包含头文件,这样修改函数接口时只需要修改一处声明。

6.1.3 分离式编译

  • 对于将函数定义在其他源文件中的情况,分离式编译使得可以只重新编译修改过的源文件,生成对象代码.obj或.o,然后链接在一起
CC -c factmain.cc # factmain.o
CC -c fact.cc # fact.o
CC factmain.o fact.o #factmain.exe
CC factmain.o fact.o -o main #main.exe

6.2 参数传递

  • 引用传递:形参是实参的别名
  • 值传递:形参与实参是两个对象

6.2.1 传值参数

  • 改动不影响实参
  • 指针形参:指针形参不影响实参,但是可以用于修改所指对象;建议用引用传递,而非指针

6.2.2 传引用参数

  • 可以避免使用拷贝,对于大的类对象或者容器对象比较合适;此外有些类类型不能拷贝,如IO
  • 可以使用引用传递返回值

6.2.3 const形参和实参

  • 如果const是顶层const,实参是常量或者非常量都可以,因此此时不能用const形参与非const形参做区分,实际是相同的参数
  • 指针或者引用的形参与const:
    • 对于底层const的指针或者引用,可以使用常量或者非常量做实参,引用也可以使用字面值做实参
    • 对于非底层const的指针或者引用,只能使用非常量,也不能使用字面值
  • 如果不修改参数值尽量使用常量引用,因为一般引用的实参选择范围更小,会在传递参数时出现不匹配,此外在不同函数之间重复调用时会出现参数不匹配

6.2.4 数组形参

  • 数组不允许拷贝,所以不能进行对数组的值传递,需要通过指针或者引用;
  • 指针的类型是数组的元素类型,而引用的类型是数组本身的类型

(1) 指针

  • 形参有三种等效的书写方式,其实质上都会被编译器视作const int*,此外,对数组大小的说明只有一个提醒作用,不具备实际效果;同样的,尽量使用const。
void print(const int*)
void print(const int[])
void print(const int[10])

(2) 数组大小

  • 通过指针传递不能获得数组大小的信息,有三种方法
  • 方法一:标记
    该方法适合于数组末尾有特殊标记的数组,比如(const) char*的末尾是空字符。
void print(const char* p)
{  
  if(p)//判单空指针
    while(*p)//判断空字符
      std::cout<<*p++;
}
  • 方法二:迭代器
    该方法通过传入begin以及end指针来实现
void print(const int* begin, const int* end)
{
  while(begin!=end)
    cout<<*begin++;
}
  • 方法三:尺寸形参
    该方法直接传入数组大小

(3) 应用传递

  • 引用传递的数组大小要求只能使用对应大小的数组作为实参,因此限制了自由。
void print(int(&arr)[10])
 {
  for(auto item:arr)
    cout<<item;
}

(4)传递多维数组

void print(int (*arr)[10], int rowsize)
void print(int arr[][10], int rowsize)

6.2.5 main:处理命令行选项

  • 第一个参数是字符串数量,第二个参数是字符串数组
  • 字符串数组的第一个值是程序名或者是空字符串,最后一个是0,其他是输入参数
int main(int argc, char *argv[])
int main(int argc, char **argv)

6.2.6 含有可变形参的函数

  • 有时函数不确定传入的实参个数
  • 如果实参个数不确定但是类型相同,可以使用initialize_list,如果类型不相同可以使用可变参数模板
  • 此外,c有省略符

(1) initialize_list

  • 标准库类型,定义在initialize_list.h中
initialize_list<T> li;
initialize_list<T> li{a,b,c,...};
l2(l1);
l2=l1;
l.size();
l.begin();//指针
l.end();//指针
  • initialize_list与vector类似,也是容器模板,但是其内容不可变
  • 对应的实参采用花括号传递
string b="bbb",c="ccc";
errmog({"aaa",b,c});

(2)省略符

  • 只能够使用c与c++的通用类型,大多数类类型的对象不可用
  • 省略符的类型不做检查
  • 逗号可选
void foo(...);
void foo(list,...);//逗号可以省略

6.3 返回类型和return

6.3.1 无返回值

6.3.2 有返回值

6.3.3返回数组指针