C++Primer练习笔记-第六章函数

第六章 函数

6.1 函数基础

局部静态对象

局部静态对象(local static object)在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被摧毁,在此期间对象所在的函数结束执行也不会对它有影响

size_t count_calls(){
    static size_t ctr = 0;
    return ++ctrj;
}
int main(){
    for (size_t i = 0; i != 10; ++i)
        cout << count_calls() << endl;
    return 0;
}

6.2 参数传递

6.2.1 传值参数

当初始化一个非引用类型的变量时,初始值被拷贝给变量,此时,对变量的改动不会影响初始值

指针形参

当执行指针拷贝操作是,拷贝的是指针的值,拷贝之后,两个指针是不同的指针。因为指针使我们可以间接地访问它所指的对象,所以通过指针可以修改它所指对象的值

#include<iostream>
using namespace std;
void reset(int *ip){
    ip = 0; // 改变了指针ip所指对象的值
}
int main(){
    int i = 42;
    reset(&i);
    cout << "i = " << i << endl;
    return 0;
}

6.2.2 传引用参数

引用形参绑定初始化它的对象,在函数调用过程中,形参仅仅是实参的另一个名字,在函数内部改变形参,也会改变实参

void reset(int &i) {
    i = 0;
}
int main(){
    int j = 42;
    reset(j);
    cout << j << endl;
    return 0;
}
使用引用避免拷贝

拷贝大的类类型对象或者容器对象比较低效,有的类类型(包括IO类型在内)根本不支持拷贝操作,当各种类型不支持拷贝操作时,函数只能通过引用形参访问该类型的对象

例如对string对象的访问

bool isShorter(const string &s1, const string &s2) {
    return s1.size() < s2.size();
}
使用引用形参返回额外信息

一个函数只能返回一个值,然而有时函数需要同时返回多个值,引用形参为我们一次返回多个结果提供了有效的途径

6.3.3 返回数组指针

数组不能被拷贝,所以函数不能返回数组。不过,函数可以返回数组的指针或引用。

直接的方法是使用类型别名

typedef int arrT[10];   // arrT是一个类型别名,它表示的是含有10个整数的数组

using arrT = int[10];   // arrT的等价声明
arrT* func(int i);  // func 返回一个指向含有10个整数的指针
声明一个返回数组指针的函数

在声明 func 时不使用类型别名,我们必须牢记被定义的名字后面数组的维度

int arr[10];    // arr是一个含有10个整数的数组
int *p1[10];    // p1是一个含有10个指针的数组
int (*p2)[10] = &arr;   // p2是一个指针,它指向含有10个整数的数组
int (*func(int i))[10];
  • func(int i) 表示调用 func 函数需要一个 int 类型的实参
  • (*func(int i)) 意味着我们可以对函数调用的结果执行解引用操作
  • (*func(int i))[10] 表示解引用将得到一个大小是10的数组
  • int (*func(int i))[10] 表示数组中的元素是int类型
使用尾置返回类型

尾置类型跟在形参后面并以一个 -> 符号开头。为了表示函数真正的返回类型跟在形参列表之后,我们在本应该出现返回类型的地方放置一个 auto

auto func(int i) -> int(*)[10];
使用 decltype

如果知道函数返回的指针指向那个数组,可以使用 decltype 关键字声明返回类型。

int odd[] = {1, 3, 5, 7, 9};
int even[] = {0, 2, 4, 6, 8};
// 返回一个指针,该指针指向含有5个整数的数组
decltype(odd) *arrPrt(int i)
{
    return (i % 2) ? &odd : &even;
}

有一个地方需要注意的是:decltype 并不负责吧数组转换为对应的指针,所以 decltype 的结果是个数组,想要表示 arrPtr 返回指针还必须在函数声明时加一个 * 符号

6.4 函数重载

  • 编译器根据实参的类型确定调用哪一个函数

  • 对于重载的函数来说,它们应该在形参数量或形参类型上有所不同

  • 不允许两个函数除了返回类型外其他要素都相同

  • 顶层const不影响传入函数的对象,一个拥有顶层const的形参无法和另一个没有顶层const的形参区分开来

    Record lookup(Phone);
    Record lookup(const Phone); // 重复定义了 Record lookup(Phone)
    
    Record lookup(Phone*);
    Record lookup(Phone* const); // 重复定义了 Record lookup(Phone*)
    
  • 如果形参是某种类型的指针或引用,则通过区分其指向的是常量对象还是非常量对象可以实现函数重载,此时的const底层

    Record lookup(Account&);    // 函数作用于Acount的引用
    Record lookup(const Account&);  // 新函数,作用于常量引用
    
    Record lookup(Account*);    // 函数作用于Acount的指针
    Record lookup(const Account*);  // 新函数,作用于常量的指针
    

6.5 特殊用途语言特性

6.5.1 默认实参

默认实参:调用含有默认实参的函数时,可以包含该实参,也可以省略该实参

typedef string::size_type sz;
string screen(sz ht = 24, sz wid = 80, char backgrnd = ' ');

使用默认实参调用函数: 只要在调用函数的时候省略该实参就可以了

函数调用时按其位置解析,默认实参负责填补函数调用缺少的尾部实参

默认实参声明: 函数的声明通常是放在头文件中,并且一个函数只声明一次,但是多次声明同一个函数也是合法的。不过有一点需要注意,在给定的作用域中一个形参只能被赋予一次默认实参。换句话说,函数的后续声明只能为之前那些没有默认值的形参添加默认实参,并且该形参右侧的所以形参必须都有默认值。

// 表示高度和宽度的形参没有默认值
string screen(sz, sz, char = ' ');
// 不能修改一个已经存在的默认值
string screen(sz, sz, char = '*');  // 错误:重复声明
string screen(sz = 24, sz =80, char);  // 正确:添加默认实参

课后练习

pratice 6.4

编写一个与用户交互的函数,要求用户输入一个数字,计算生成该数字的阶乘。在main函数中调用该函数

#include<iostream>
using namespace std;
/*
 * 阶乘函数
 */
int fact (int val){
    int ret = 1;s
    while(val > 1)
        ret *= val--;
    return ret;
}
/*
 * 绝对值函数
 */
int abs(int val){
    return (val>=0?val:-val);
}
int main(){
    int val;
    cin >> val;
    cout << val << "! is " << fact(val) << endl;
    return 0;
}

pratice 6.10

编写一个函数,使用指针形参交互两个整数的值。

指针形参参考

#include<iostream>
using namespace std;
void swap(int *p1, int *p2){
    int temp = *p1;
    *p1 = *p2;
    *p2 = *p1;
}
int main(){
    int a=23, b=32;
    swap(a, b);
    cout << a << ' ' << b << endl;
    return 0;
}

pratice 6.11

改写并验证你自己的reset函数,使其作用于引用类型的参数

#include<iostream>
using namespace std;
void reset(int &i) {
    i = 0;
}
int main() {
    int j = 42;
    reset(j);
    cout << j << endl;
    return 0;
}

pratice 6.25

编写一个main函数,令其接受两个实参。把实参的内容连接成一个string对象并输出出来

#include<iostream>
using namespace std;
int main(int argc, char **argv) {
    string s;
    for(int i=0;i<argc;i++){
        s.append(argv[i]);
        s.append(" ");
    }
    cout << s << endl;
    cout << argc << endl;
}

pratice 6.36

编写一个函数声明,使其返回数组的引用并且该数组包含10个string对象。不要使用尾置返回类型、decltype或者类型别名

#include<iostream>
#include<string>
using namespace std;
string odd[10] = {"life", "is", "like", "a", "box", "of", "chocolate"};
string even[10] = {"Do", "not", "ANSWER", "!", "Do", "not", "ANSWER", "!"};
int main(){
    string (*func(int i))[10];
    int i;
    cin >> i;
    string (*a)[10] = func(i);
    for(int j=0;j<10;j++)
        cout << (*a)[j] << endl;
    cout << endl;
    return 0;
}

string (*func(int i))[10]{
    return (i % 2) ? &odd : &even;
}

输入数字ii为奇数则输出第一个字符串,否则输出第二个字符串

pratice 6.37

为上一题的函数再写三个声明,一个使用类型别名,另一个使用尾置返回类型,最后一个使用decltype关键字

#include<iostream>
#include<string>
using namespace std;
typedef string arrStr[10];
string odd[10] = {"life", "is", "like", "a", "box", "of", "chocolate"};
string even[10] = {"Do", "not", "ANSWER", "!", "Do", "not", "ANSWER", "!"};
int main(){
    string (*func(int i))[10]; // 直接声明
    // arrStr* func1(int i);   // 类型别名声明
    // decltype(odd) *func2(int i);    // decltype 声明
    // auto func3(int i) -> string(*)[10];
    int i;
    cin >> i;
    string (*a)[10] = func(i);
    for(int j=0;j<10;j++)
        cout << (*a)[j] << endl;
    cout << endl;
    return 0;
}
// 直接声明
string (*func(int i))[10]{
    return (i % 2) ? &odd : &even;
}
// 类型别名声明
arrStr* func1(int i){
    return (i % 2) ? &odd : &even;
}
// decltype 声明
decltype(odd) *func2(int i){
    return (i % 2) ? &odd : &even;
}
// 尾置返回类型
auto func3(int i) -> string(*)[10]
{
    return (i % 2) ? &odd : &even;
}

pratice 6.38

修改arrPtr函数,使其返回数组的引用

直接在前两题的基础上进行修改,主要是将*修改为&return语句中的&去掉,调用后的数组使用不要*解引用

#include<iostream>
#include<string>
using namespace std;
typedef string arrStr[10];
string odd[10] = {"life", "is", "like", "a", "box", "of", "chocolate"};
string even[10] = {"Do", "not", "ANSWER", "!", "Do", "not", "ANSWER", "!"};
int main(){
    string (&func(int i))[10]; // 直接声明
    // arrStr& func1(int i);   // 类型别名声明
    // decltype(odd) &func2(int i);    // decltype 声明
    // auto func3(int i) -> string(&)[10];
    int i;
    cin >> i;
    string (&a)[10] = func(i);
    for(int j=0;j<10;j++)
        cout << a[j] << endl;
    cout << endl;
    return 0;
}
// 直接声明
string (&func(int i))[10]{
    return (i % 2) ? odd : even;
}
// 类型别名声明
arrStr& func1(int i){
    return (i % 2) ? odd : even;
}
// decltype 声明
decltype(odd) &func2(int i){
    return (i % 2) ? odd : even;
}
// 尾置返回类型
auto func3(int i) -> string(&)[10]
{
    return (i % 2) ? odd : even;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值