C++primer第五版第六章学习笔记

  1. 形参实参

    void func(int a, char c) // 形参
     {
    
     }
    
     int main()
     {
         func(1, 'a'); // 实参,与形参的类型、数量相匹配
         return 0;
     }

  2. 局部自动对象作用域和生命周期。不要让函数返回局部自动对象的引用或者指针,否则出了该作用域后,局部自动对象自动销毁之后,主调函数拿到的就是未定义的值。

  3. 局部静态变量:

    • 内置基本类型的局部静态变量如果没有显示地初始化,就会被初始为 0,且只会初始化一次
    • 局部静态变量会被存储到存储器的全局量区,生命周期贯穿函数调用以及之后的实际直到程序终止才会被销毁
    • 虽然局部变量的生命周期超出函数的作用域范围,但是局部变量只能在其所在函数的作用域范围内使用

      #include <iostream>
      
         using namespace std;
      
         int counter()
         {
             static int static_cnt = 0;
             int stack_i = ++static_cnt;
             return stack_i;
         }
      
         int main()
         {
             for (int i = 0; i < 5; ++i) {
                 cout << counter() << endl;
             }
             return 0;
         }

  4. 函数声明(函数原型,)不包含函数体(返回值类型还是需要的),且形参列表可以只保留参数的类型而省略参数名称,最后需要以";"结束。函数在被调用之前一定要被声明或被定义。
    // 函数的声明,必须要写在函数第一次被调用之前。
     // 这部分的代码可放到头文件中,用的时候include进来就可以了(分离式编译)
     int func(char, int);
    
     // 只要函数在被调用前声明了,那函数的定义可以写在声明之后的任意的位置,
     // 如这里的func就可以在声明之后、main函数之前定义;
     // 如果func没有在调用前声明,则必须要在被调用前定义(相当于把main函数后面的func那段代码放在这里实现)
    
     int main() {
         return func('a', '1'); // 函数的调用,虽然func在main后定义,但是因为之前对func进行了声明,所以编译器知道这个函数的三要素是啥
     }
    
     /*
      * 函数的定义的参数列表中各参数的类型、数量以及位置等需要和声明时的相匹配
      */
     int func(char c, int i)
     {
         // do something
         return 0;
     }


  5. 值传递、引用传递

    • 值传递传的是变量的拷贝,而不是原始的值
    • 在修改指针参数指向的内存空间存储的数据时,应使用操作符*
    • 操作引用参数实际操作的是参数所引用的对象,使用引用传递可以避免拷贝,比如说如果vector对象中存储了大量的元素,而其作为参数传入时不需要进行修改,则可以传递引用,这样就避免了拷贝其中的大量元素,提高效率
    • 使用引用参数可以让函数返回额外的信息
    • 函数的顶层const形参会被忽略掉,重载时需注意

      #include <iostream>
      
         using namespace std;
      
         /**
          * 传进来的是main函数中变量a和b的值的拷贝 c1, c2
          * 交换的也是值的拷贝,并没有改变a和b内存空间中的数据,
          * 所以此函数并不能交换a和b的值
          */
         void swap1(char c1, char c2)
         {
             char tmp = c1;
             c1 = c2;
             c2 = tmp;
         }
      
         /**
          * 传进来的是main函数中变量a和b的内存地址的拷贝 cp1, cp2
          * 交换的也是内存地址的拷贝,并没有改变a和b内存空间中的数据,
          * 所以此函数并不能交换a和b的值
          */
         void swap2(char *cp1, char *cp2)
         {
             cout << "in swap2: *cp1=" << *cp1 << ", *cp2=" << *cp2 << endl;
      
             char *tmp = cp1;
             cp1 = cp2;
             cp2 = tmp;
      
             cout << "out swap2: *cp1=" << *cp1 << ", *cp2=" << *cp2 << endl;
         }
      
         /**
          * 传进来的是main函数中变量a和b的内存地址的拷贝 cp1, cp2
          * 然后通过操作符*来更新内存地址中的数据,所以此函数可以成功交换a和b的值
          */
         void swap3(char *cp1, char *cp2)
         {
             cout << "in swap3: *cp1=" << *cp1 << ", *cp2=" << *cp2 << endl;
      
             // 取c1指向的内存空间中的数据赋值给临时变量tmp
             char tmp = *cp1;
             // 取cp2指向的内存空间中的数据赋值到cp1指向的内存空间中,覆盖原有数据
             *cp1 = *cp2;
             // 将tmp的值赋值到cp2指向的内存空间中,覆盖原有数据
             *cp2 = tmp;
      
             cout << "out swap3: *cp1=" << *cp1 << ", *cp2=" << *cp2 << endl;
         }
      
         /**
          * 传进来的是main函数中变量a和b的引用cr1, cr2
          * cr1, cr2直接操作的是其引用的对象,所以此函数可以成功交换a和b的值
          */
         void swap4(char &cr1, char &cr2)
         {
             char tmp = cr1;
             cr1 = cr2;
             cr2 = tmp;
         }
      
         /**
          * 交换指针,过程是一样的,只是会这里需要传指针的指针进来
          */
         void swap_pointer(char **cp1, char **cp2)
         {
             char *tmp = *cp1;
             *cp1 = *cp2;
             *cp2 = tmp;
         }
      
         int main()
         {
             char a = 'A';
             char b = 'B';
             cout << "origin: a=" << a << ", b=" << b << endl << endl;
      
             swap1(a, b);
             cout << "after swap1: a=" << a << ", b=" << b << endl << endl;
      
             swap2(&a, &b);
             cout << "after swap2: a=" << a << ", b=" << b << endl << endl;
      
             swap3(&a, &b);
             cout << "after swap3: a=" << a << ", b=" << b << endl << endl;
      
             // 重置 a, b 为swap3交换之前的原值
             a = 'A';
             b = 'B';
             swap4(a, b);
             cout << "after swap4: a=" << a << ", b=" << b << endl << endl;
      
             // 重置 a, b 为swap4交换之前的原值
             a = 'A';
             b = 'B';
             char *pa = &a;
             char *pb = &b;
             printf("before swap_pointer: pa=%p, pb=%p\n", pa, pb);
             swap_pointer(&pa, &pb);
             printf("after swap_pointer: pa=%p, pb=%p\n", pa, pb);
      
             return 0;
         }

  6. 动态参数列表:void func(string msg, ...); 常见的应用场景:日志的打印
  7. c++11新标准允许返回花括号包围的值列表:
     vector<string> test()
     {
         return {"hello", "world"};
     }


  8. 递归,调用函数自身,main函数不能调用它自己。应用场景:虽然很多书的例子都是用的阶乘或者是斐波拉数列,但是实际工作中除了就是做算法的人,因为涉及到性能的问题,平时应该都很少用递归,我自己也只在遍历文件目录的时候用过递归。
     // 伪代码
     void list_dir(string dir)
     {
         files = traverse_current_dir(dir); // 遍历当前目录下的文件或者目录
         for path in files:
             print path
             if path is directory: // 如果文件是目录,则调用自身遍历这个目录
                 list_dir(path);
     }


  9. 函数的重载:函数名与返回值一致,参数列表不一致。main函数不能重载。在调用时程序会自动根据参数列表识别被调用的函数是哪一个(重载确定)。
    int get_user(int id);
     int get_user(string name, string mail);
    
     int main()
     {
         get_user(100); // 调用形参列表是 (int) 的
         get_user("cat", "littlefawn@163.com"); // 调用形参列表是 (string, string)的
    
         return 0;
     }


  10. 默认参数,在声明时使用的默认参数不能时局部变量
  11. 内联函数:编译时会在调用点上进行展开,避免函数调用的开销
    #include <iostream>
    
    using namespace std;
    
    // 在函数声明的返回值之前加上关键字 inline即可
    inline string short_str(const string &str1, const string &str2) {
        return str1.size() > str2.size() ? str1 : str2;
    }
    
    int main()
    {
        string s1 = "Hi";
        string s2 = "world";
        // short_str会被展开为:()
        cout << short_str(s1.size() > s2.size() ? s1 : s2) << endl;
    
        return 0;
    }

  12. constexpr函数:隐式的内联函数,除了需要遵守内联函数的约定,其返回值的类型以及所有的参数类型都必须是字面值类型,而且函数体内有且只有一条返回语句。
    #include <iostream>
    
    using namespace std;
    
    constexpr int MIN_TO_SEC(int minutes)
    {
        return minutes * 60;
    }
    
    int main()
    {
        const int one_hour_minutes = 60;
    
        cout << one_hour_minutes << " minutes = " << MIN_TO_SEC(one_hour_minutes) << " seconds" << endl;
    
        return 0;
    }


  13. 函数指针:指向函数的指针,当所指向的函数是重载函数时,需要明确指定应选用哪个函数
    #include <iostream>
    
    using namespace std;
    
    string short_str(const string &str1, const string &str2) {
        return str1.size() > str2.size() ? str1 : str2;
    }
    
    string short_str(const string &str1) {
        return str1;
    }
    
    // 参数p为函数指针
    void show(const string &str1, const string &str2, string (*p)(const string &, const string &)) {
        cout << p(str1, str2) << endl;
    }
    
    // 返回指向函数的指针
    using SSP = string (*)(const string &);
    SSP get_function()
    {
        return &short_str;
    }
    
    int main()
    {
        string (*ssp)(const string &, const string &);
    
        ssp = &short_str; // 如果只有一个short_str时,只需要直接赋值即可
        cout << ssp("Hi", "world") << endl;
    
        string (*ssp2)(const string &) = &short_str; // 存在重载函数时,上下文需明确指定应选用哪个函数
        cout << ssp2("Hi") << endl;
    
        show("my", "god", &short_str);
    
        cout << get_function()("good") << endl;
    
        return 0;
    }




  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值