C++ 基础

C++

概述

C++ 是 C 的超集。
C++ 诞生于贝尔实验室,开发者是 Bjarne Stroustrup (本贾尼·斯特劳斯特卢普)。

C++ 融合了三种不同的编程方式

  • C 语言代表的过程性语言
  • 面向对象 (OOP, Object Oriented Programming)
  • 泛型编程

面向过程

  • 以过程为中心的编程思想
    • 功能分解
    • 自顶向下
    • 逐层细化
  • 缺点
    • 不符合人的思维习惯
    • 重用性低,维护困难

面向对象 (OOP) 的三大特性

面向对象中,算法和数据结构被看作一个整体,称作对象。
使用代码模块,增加了代码的重用率,便于开发、维护。

  • 封装
    • 把客观的事物封装成抽象的类
  • 继承
    • 使得对象可以继承另一类对象的特征和能力
    • 避免公用代码的重复开发,减少代码和数据冗余
  • 多态
    • 一个接口,多种方法

可移植性和标准

  • 美国国家标准局 (American National Standards Institute, ANSI) 在1998年制定出第一套 C++ 标准,叫做 C++98
  • 2011 年又新增了 C++11
  • 2020年又新增了 C++20

第一个程序:hello world

“`c++
#include <iostream> // 标准输入输出流,相当于 <stdio.h>
using namespace std; // 使用标准命名空间

// 第一个 C++ 程序
void test01()
{
// cout:标准输出流对象
// <<:在 cout 后拼接输出的内容
// endl:end line,刷新缓冲区,并且换行
cout << "hello world" << endl; // hello world
}

“`

C++ 对 C 的扩展

作用域控制增强

  • 双冒号作用域运算符 (::)
    • 代表作用域

    • 如果前面什么都不添加,代表全局作用域

    • 同名的变量,默认局部变量优先级更高

    • 示例

      “`c++
      int atk = 1000;

      void test02()
      {
      int atk = 2000;
      cout << "atk = " << atk << endl; // 2000
      ::cout << "atk = " << ::atk << ::endl; // 1000
      std::cout << "atk = " << ::atk << std::endl; // 1000
      }

      “`

  • namespace (命名空间)

    主要用途是解决名称冲突。命名空间下可以存放:变量、函数、结构体、类。

    • 命名空间必须要声明在全局作用域,不可以命名到局部作用域

    • 命名空间可以嵌套命名空间

    • 命名空间是开放的,可以随时给命名空间添加新的成员

    • 命名空间可以是匿名的

    • 命名空间可以起别名

    • 示例

      “`c++

      <h1>define _CRT_SECURE_NO_WARNINGS</h1>

      <h1>include</h1>

      using namespace std;

      // 主要用途是,解决名称冲突
      namespace A
      {
      int atk = 10;
      }

      // 命名空间下可以存放: 变量、函数、结构体、类
      namespace B
      {
      int atk = 20;
      void func(); // 声明和实现分离
      struct Person
      {};
      class Animal
      {};
      }

      void B::func() // 函数实现
      {}

      // 命名空间可以嵌套命名空间
      namespace C
      {
      int a = 10;
      namespace D
      {
      int d = 20;
      }
      }

      // 命名空间是开放的,可以随时给命名空间添加新的成员
      namespace C
      {
      int b = 20;
      }

      // 命名空间可以是匿名的
      namespace
      {
      int cc = 10; // 匿名空间中,相当于 static int cc = 10;
      int dd = 20;
      }

      //
      void test01()
      {
      cout << A::atk << endl; // 10
      cout << B::atk << endl; // 20

      <pre><code>// 命名空间必须要声明在全局作用域
      //namespace C {}; // Error! 不可以命名到局部作用域

      cout << C::a << endl; // 10
      cout << C::b << endl; // 20

      cout << ::cc << endl; // 10
      cout << dd << endl; // 20

      // 命名空间可以起别名
      namespace aliasname = C;
      cout << aliasname::b << endl; // 20
      </code></pre>

      }

      int main(void)
      {
      test01();

      <pre><code>system("pause");
      return EXIT_SUCCESS;
      </code></pre>

      }

      “`

  • using 声明和编译指令

    • using 声明
      • 语法

        “`c++
        using 命名空间名称::元素
        “`

      • 当 using 声明与就近原则同时出现,会出错,要尽量避免

    • using 编译指令

      • 当 using 的编译指令与就近原则同时出现,优先使用就近原则
      • 当 using 编译指令有多个,需要加作用域区分
    • 示例

      “`c++

      <h1>define _CRT_SECURE_NO_WARNINGS</h1>

      <h1>include</h1>

      using namespace std;

      namespace A
      {
      int a = 10;
      }

      namespace B
      {
      int a = 111;
      }

      void test01()
      {
      int a = 20;

      <pre><code>// 1. 当 using 声明与就近原则同时出现,会出错,要尽量避免
      //using A::a; // Error!
      cout << a << endl; //

      // 2. 当 using 的编译指令与就近原则同时出现,优先使用就近原则
      using namespace A;
      cout << a << endl; // 20

      // 3. 当 using 编译指令有多个,需要加作用域区分
      cout << A::a << endl; // 10
      cout << B::a << endl; // 111
      </code></pre>

      }

      int main(void)
      {
      test01();

      <pre><code>system("pause");
      return 0;
      </code></pre>

      }

      “`

检测增强

  • 全局变量检测增强

    “`c++
    int a;
    int a = 10; // 重定义
    “`

    • C++ 会检测出重定义
  • 函数的检测增强
    • 会检查所有返回值、形参类型、函数调用的参数个数

    • 对比

      • C 语言中,int func() 表示返回值是 int,接受任意参数;int func(void) 表示返回值为 int 的无参函数
      • C++ 中,int func() 和 int func(void) 具有相同的意义,都表示返回值为 int 的无参函数
    • 示例

      “`c++
      getRet(w, h)
      {
      return w * h;
      }

      void test01()
      {
      printf("%d\n", getRet(10, 20, 30)); // 200
      }

      “`

  • 类型转换检测增强

    • C++

    • C++ 中,不同类型的变量一般不能直接赋值,需要相应的强转

    • 示例

      “`c++
      void test02()
      {
      //char* p = malloc(64); // 报错
      char* p = (char *)malloc(64);
      }
      “`

类型增强

  • struct 类型增强

    • C 语言下的结构体中不允许有函数,C++ 可以在结构体中放函数

    • 创建结构体变量,可以省略关键字 struct

    • 示例

      “`c++
      struct Person
      {
      int age;
      void func()
      {
      age++;
      }
      };

      void test03()
      {
      Person p; // 创建结构体变量,可以省略关键字 struct
      p.age = 100;
      }

      “`

  • 新增 bool 类型关键字

    • C 语言没有这个数据类型,C++ 有这个数据类型,默认为 0
      • C99 标准中也有 bool 类型,头文件 stdbool.h
    • bool 类型只有两个值:true (整数 1) 和 false (整数 0)
      • 非 0 值会自动都转成 1
    • 只占 1 个字节
      • sizeof(bool) = 1
    • 示例

      “`c++
      bool flag;

      void test04()
      {
      cout << sizeof(bool) << endl; // 1
      cout << flag < b ? a : b;
      printf("%d\n", ret1); // 2

      int ret2 = a > b ? a : b = 100; // C 语言下返回的是值;C++ 返回的是变量
      printf("%d\n", ret1); // 2

      printf("%d, %d\n", a, b); // 1, 100
      }

      “`

  • C++ 返回的是变量本身 (引用),为左值,可以赋值

    “`c++
    // 三目运算符增强
    void test05()
    {
    int a = 1;
    int b = 2;

    <pre><code>int ret1 = a > b ? a : b;
    printf("%d\n", ret1); // 2

    //int ret2 = a > b ? a : b = 100; // C 语言下返回的是值;C++ 返回的是变量
    int ret3 = *(a > b ? &a : &b) = 100;
    printf("%d\n", ret1); // 2

    printf("%d, %d\n", a, b); // 1, 100
    </code></pre>

    }

    “`

  • 左值和右值

    • 可以放在赋值操作符左边的是左值,可以放到赋值操作符右边的是右值
    • 左值为 Lvalue,L 代表 Location,表示内存可以寻址,可以赋值
    • 右值为 Rvalue,R 代表 Read,就是可以知道它的值
    • 例如:int temp = 10
      • temp 在内存中有地址,10 没有,但是可以 Read 到它的值

const 增强

  • 是否为 const 分配内存
    • C 语言中的 const
      • 被认为是一个全局只读变量,会给 const 分配内存

      • 示例

        const int arrSize = 10;
        int arr[arrSize];
        

        Note: 上面代码看似合理,但是是错误的。因为 arrSize 占用某块内存,所以 C 编译器不知道它在编译时的值是多少。

    • C++ 中的 const

      • 一个 const 不必创建内存空间
      • 是否为 const 常量分配内存空间依赖于如何使用
  • 全局和局部 const
    • C 语言下
      • 全局 const
        • 直接修改,失败;间接修改,语法通过,运行失败
        • 被存储到只读数据段,所以不可修改
      • 局部 const
        • 直接修改,失败;间接修改,成功
        • 存储在堆栈区,不能通过变量直接修改,但是可以跳过编译器的检查,通过指针间接修改
      • 示例

        “`c++
        int m = 10;
        //const int m = 10;

        void test06()
        {
        //m = 20;
        int* p = &m;
        *p = 20;

        <pre><code>printf("%d\n", m); // 20

        const int mb = 11;
        //mb = 22;
        int* mp = &mb;
        *mp = 22;

        printf("%d\n", mb); // 22
        </code></pre>

        }

        “`

    • C++ 下

      const 修饰的变量,称为常量,可以初始化数组。

      • 全局 const
        • 和 C 语言结论一样
        • 当声明 extern 或对变量取地址时,编译器会分配存储地址,变量存储在只读数据段,所以不可修改
      • 局部 const
        • 直接修改,失败;间接修改,失败
          • 使用了符号表,利用了临时指针,不是原指针
        • 要区别对待
          • 对于基础数据类型,类似 const int a = 10 这种,编译器会把它放到符号表中,不分配内存,当对其取地址时,会分配内存
            • 分配内存的情况
              • 对 const 变量取地址,会分配临时内存

              • 使用普通变量,初始化 const 变量

                “`c++

                <h1>include</h1>

                “`

              • 对于自定义数据类型,会分配内存

          • 对于基础数据类型,如果用一个变量初始化 const 变量,类似 const int a = b,那么也会给 a 分配内存

          • 对于自定义数据类型,例如,类,也会分配内存

      • 示例

        “`c++
        int m = 10;
        //const int m = 10;

        void test06()
        {
        //m = 20;
        int* p = &m;
        *p = 20;

        <pre><code>printf("%d\n", m); // 20

        const int mb = 11;
        //mb = 22;
        int* mp = (int*)&mb; // 报错
        *mp = 22;

        printf("%d\n", mb); // 11
        </code></pre>

        }

        “`

  • 链接属性

    • C 语言下
      • const 修饰的全局变量默认是外部链接属性
      • C 文件中,如果两个文件中都有 const int a,会报重定义错误,而 C++ 不会报错
    • C++ 下
      • const 修饰的全局变量默认是内部链接属性
        • 可以使用 extern 提高作用域,变为外部链接

          “`c++
          extern const int g_a = 100;
          “`

  • 尽量用 const 代替 #define

    • 宏是不被编译的,因为在预处理阶段已经被替换了

    • define 出的宏常量,没有数据类型,不重视作用域

      “`c++
      //#define MAX 1024
      //#udefine MAX
      const int max = 1024; // 用常量代替宏
      “`

      • 编译报错时,可能提到了 1024,但是没有提到 MAX,所以建议用常量代替宏
    • const 和 define 区别
      • const 有类型,可以进行编译器安全检查;define 无类型,不进行类型检查
      • const 有作用域,而 define 不重视作用域
  • 能否用变量定义数组
    int a = 10;
    int arr[a];
    
    • VS 中不可以
    • Linux GCC 下,C99 可以
  • 示例

    “`c++

    <h1>define _CRT_SECURE_NO_WARNINGS</h1>

    <h1>include</h1>

    <h1>include</h1>

    using namespace std;

    // 对 const 变量取地址,会分配临时内存
    void test01()
    {
    const int a = 10;
    int* p = (int*)&a;
    }

    // 使用普通变量,初始化 const 变量
    void test02()
    {
    int a = 10;
    const int b = a;

    <pre><code>int* p = (int *)&b;
    *p = 100;

    cout << a << endl; // 10
    cout << b << endl; // 100
    cout << *p <age = 10;

    cout << "name: " << p.name << ", age: " << p.age << endl;
    </code></pre>

    }

    int main(void)
    {
    //test01();
    //test02();
    test03();

    <pre><code>system("pause");
    return EXIT_SUCCESS;
    </code></pre>

    }

    “`

C++ 新增

引用 (reference)

  • 基本语法

    • 语法

      “`c++
      类型 (与原名的类型必须一致) &别名 = 原名
      “`

      • 引用的目的,起别名
      • &:在此不是求地址运算,而是起标识作用
      • 必须在声明引用变量时进行初始化
      • 引用初始化之后不能改变
      • 不能有 NULL 引用。必须确保引用是和一块合法的存储单元关联
      • 可以建立对数组的引用
    • 注意事项
      • 引用必须初始化
      • 引用一旦初始化后,就不可以引向其他变量
    • 对数组建立引用
      • 直接建立引用
      • 先定义出数组类型,再通过类型定义引用
    • 引用的本质
      • 在 C++ 内部实现是一个指针常量
    • 示例

      “`c++

      <h1>define _CRT_SECURE_NO_WARNINGS</h1>

      <h1>include</h1>

      using namespace std;

      // 引用的基本语法
      void test01()
      {
      int a = 10;
      int& b = a; // 引用
      b = 100;

      <pre><code>cout << "a = " << a << ", b = " << b << endl; // a = 100, b = 100
      </code></pre>

      }

      // 注意事项
      void test02()
      {
      int a = 10;
      //int& b; // 引用必须初始化
      int& b = a;

      <pre><code>int c = 20;
      //&b = c;
      b = c; // 引用一旦初始化后,就不可以引向其他变量

      cout << "a = " << a;
      cout << ", b = " << b;
      cout << ", c = " << c << endl; // a = 20, b = 20, c = 20
      </code></pre>

      }

      // 对数组建立引用
      void test03()
      {
      int arr[10];
      int(&pArr1)[10] = arr; // 直接建立引用

      <pre><code>typedef int(ARRAY_TYPE)[10]; // 先定义出数组类型,再通过类型定义引用
      ARRAY_TYPE& pArr2 = arr;

      for (int i = 0; i < 10; i++)
      {
      arr[i] = i + 10;
      cout << pArr1[i] << " " << pArr2[i] << endl;
      }
      </code></pre>

      }

      // 引用的本质是一个指针常量
      void testFunc(int& ref) //发现是引用,转换为 int* const ref = &a;
      {
      ref = 100; // ref 是引用,转换为 *ref = 100
      }

      void test04()
      {
      int a = 10;
      int& aRef = a; // 自动转换为 int* const aRef = &a; 这也能说明引用为什么必须初始化
      aRef = 20; // 内部发现 aRef 是引用,自动转换为: *aRef = 20;
      cout << "a = " << a << ", aRef = " << aRef << endl; // a = 20, aRef = 20

      <pre><code>testFunc(a);
      </code></pre>

      }

      int main(void)
      {
      test01();
      test02();
      test03();
      test04();

      <pre><code>system("pause");
      return EXIT_SUCCESS;
      </code></pre>

      }

      “`

  • 函数中的引用

    • 参数的传递方式
      • 值传递

      • 地址传递

      • 引用传递 (C++)

        • 函数调用时,传递的参数不必加 & 符
        • 在被调函数中,不必在参数前加 * 符
        • C++ 主张用引用传递取代地址传递方式
      • 示例

        “`c++

        <h1>define _CRT_SECURE_NO_WARNINGS</h1>

        <h1>include</h1>

        using namespace std;

        // 1.值传递
        void swap1(int a, int b) // 交换数值
        {
        int temp = a;
        a = b;
        b = temp;
        }

        // 2.地址传递
        void swap2(int* a, int* b)
        {
        int temp = *a;
        *a = *b;
        *b = temp;
        }

        // 3.引用传递
        void swap3(int& a, int& b)
        {
        int temp = a;
        a = b;
        b = temp;
        }

        int main(void)
        {
        int a = 10;
        int b = 20;

        <pre><code>swap1(10, 20); // a = 20, b = 10
        cout << "a = " << a << ", b = " << b << endl; // a = 10, b = 20

        swap2(&a, &b);
        cout << "a = " << a << ", b = " << b << endl; // a = 20, b = 10

        swap3(a, b);
        cout << "a = " << a << ", b = " << b << endl; // a = 10, b = 20

        system("pause");
        return EXIT_SUCCESS;
        </code></pre>

        }

        “`

    • 返回值引用的注意事项

      • 引用必须引一块合法的内存空间
        • 示意图:int& a = 10;
          C++
      • 不要返回局部变量的引用

      • 当函数返回值是引用时,那么函数的调用可以作为左值进行运算

      • 示例

        “`c++

        <h1>define _CRT_SECURE_NO_WARNINGS</h1>

        <h1>include</h1>

        using namespace std;

        int& func1()
        {
        int a = 10;
        return a;
        }

        int& func2()
        {
        static int a = 10;
        return a;
        }

        void test01()
        {
        // 引用必须引一块合法的内存空间
        //int& a = 10; // Error!

        <pre><code>// 不要返回局部变量的引用
        int& ref1 = func1();
        cout << "ref1 = " << ref1 << endl; // ref1 = 10
        cout << "ref1 = " << ref1 << endl; // ref1 = 2026084744

        int& ref2 = func2();
        cout << "ref2 = " << ref2 << endl; // ref2 = 10
        cout << "ref2 = " << ref2 << endl; // ref2 = 10
        cout << "ref2 = " << ref2 << endl; // ref2 = 10

        // 当函数返回值是引用时,那么函数的调用可以作为左值进行运算
        func2() = 1000;
        cout << "ref2 = " << ref2 << endl; // ref2 = 1000
        </code></pre>

        }

        int main(void)
        {
        test01();

        <pre><code>system("pause");
        return EXIT_SUCCESS;
        </code></pre>

        }

        “`

  • 指针的引用

    • 利用引用简化指针

    • 可以直接用同级指针的引用,给同级指针分配空间

    • 示例

      “`c++

      <h1>define _CRT_SECURE_NO_WARNINGS</h1>

      <h1>include</h1>

      using namespace std;

      struct Person
      {
      int age;
      };

      // C 语言的方法
      void allocateSpace1(Person **p)
      {
      //struct Person* p = (struct Person<em>)malloc(sizeof(struct Person));
      *p = (Person</em>)malloc(sizeof(Person));
      (*p)->age = 10;
      }

      // 利用引用简化指针
      void allocateSpace2(Person* &pp) // 可以直接用同级指针的引用,给同级指针分配空间
      {
      pp = (Person*)malloc(sizeof(Person));
      pp->age = 20;
      }

      void test01()
      {
      Person* p = NULL;

      <pre><code>allocateSpace1(&p);
      cout << "age = " <age << endl; // age = 10

      allocateSpace2(p); // 可以直接用同级指针的引用,给同级指针分配空间
      cout << "age = " <age << endl; // age = 20
      </code></pre>

      }

      int main(void)
      {
      test01();

      <pre><code>system("pause");
      return EXIT_SUCCESS;
      </code></pre>

      }

      “`

  • 常量的引用

    • 语法

      “`c++
      const 类型 &别名 = 常量值
      “`

      • 字面量不能赋值给引用,但是可以赋给 const 引用
      • const 修饰的引用,不能修改
    • 常量引用的使用场景
      • 常量引用主要用在函数的形参,尤其是类的拷贝/复制构造函数
      • 将函数的形参定义为常量引用的好处
        • 引用不产生新的变量,减少形参与实参传递时的开销
        • 由于引用可能导致实参随形参改变而改变,将其定义为常量引用可以消除这种副作用 (防止误操作)
    • 示例

      “`c++

      <h1>define _CRT_SECURE_NO_WARNINGS</h1>

      <h1>include</h1>

      using namespace std;

      void test01()
      {
      //int& ref = 10; // 内存地址不合法

      <pre><code>// 加了 const 之后,相当于写成 int temp = 10; const int &ref = temp;
      const int& ref = 10;
      cout << "ref = " << ref << endl; // ref = 10

      int* p = (int *)&ref;
      *p = 20;
      cout << "ref = " << ref << endl; // ref = 20
      </code></pre>

      }

      // const 修饰函数中的形参,防止误操作
      void showValue(const int& a)
      {
      //a = 100; // Error! 不允许修改
      cout << "a = " << a << endl;
      }

      void test02()
      {
      int a = 200;

      <pre><code>showValue(a);
      cout << "a = " << a << endl; // a = 200
      </code></pre>

      }

      int main(void)
      {
      test01();
      test02();

      <pre><code>system("pause");
      return EXIT_SUCCESS;
      </code></pre>

      }

      “`

  • 组成

    • 成员变量
    • 成员函数
  • 示例

    “`c++

    <h1>define _CRT_SECURE_NO_WARNINGS</h1>

    <h1>include</h1>

    <h1>include</h1>

    using namespace std;

    const double PI = 3.14;

    // 1.设计一个类,求圆的周长
    class Circumference
    {
    public: // 公共权限
    double radius; // 半径

    <pre><code>// 获取周长
    double getCircumference()
    {
    return 2 * PI * radius;
    }

    // 获取半径
    double getRadius()
    {
    return radius;
    }

    // 设置半径
    void setRadius(double m_R)
    {
    radius = m_R;
    }
    </code></pre>

    };

    void test01()
    {
    // 实例化一个对象
    Circumference c1;

    <pre><code>// 设置半径
    c1.setRadius(10);

    cout << "半径 = " << c1.getRadius() << endl;
    cout << "周长 = " << c1.getCircumference() << endl;
    </code></pre>

    }

    // 2.设计一个学生类,属性有姓名、学号,可以给姓名和学号赋值并显示
    class Student
    {
    public:
    string name;
    string id;

    <pre><code>// 设置姓名、学号
    void setStudent(string n, string i)
    {
    name = n;
    id = i;
    }

    // 显示姓名、学号
    void showInfo()
    {
    cout << "name = " << name << endl;
    cout << "id = " << id << endl;
    }
    </code></pre>

    };

    void test02()
    {
    Student s1;
    s1.setStudent("aaa", "001");
    s1.showInfo();

    <pre><code>Student s2;
    s2.setStudent("bbb", "002");
    s2.showInfo();
    </code></pre>

    }

    int main(void)
    {
    test01();
    test02();

    <pre><code>system("pause");
    return EXIT_SUCCESS;
    </code></pre>

    }

    “`

内联函数

  • 宏函数缺陷

    • 必须要加括号
      • 即使加了括号,有些运算依然与预期不符
    • 预定义宏函数没有作用域概念,无法作为一个类的成员函数,也就是说预定义宏没有办法表示类的范围

    • 示例

      “`c++

      <h1>define _CRT_SECURE_NO_WARNINGS</h1>

      <h1>include</h1>

      using namespace std;

      // 宏函数缺陷1:必须加括号保证运算完整性
      //#define add(x, y) x + y // 70

      <h1>define add(x, y) ((x) + (y)) // 90</h1>

      // 宏函数缺陷2:即使加了括号,有些运算依然与预期不符

      <h1>define compare(a, b) (((a)<(b)) ? (a) : (b))</h1>

      // 内联函数
      // 函数的声明和实现必须同时加 inline 才算内联函数
      // 解决宏缺陷,本身是一个函数,带来宏优势,以空间换时间,在适当的时候展开
      inline void func();
      inline void func() {}

      void test01()
      {
      printf("(10+20)<em>3 = %d\n", add(10, 20) * 3); // (10+20)</em>3 = 90

      <pre><code>int a = 10;
      int b = 20;
      cout << compare(++a, b) << endl; // 预期 11,实际 12
      </code></pre>

      }

      int main(void)
      {
      test01();

      <pre><code>system("pause");
      return EXIT_SUCCESS;
      </code></pre>

      }

      “`

  • 概念

    • C++ 使用内联函数代替宏函数
      • 关键字:inline
      • 内联函数是一个真正的函数,具有普通函数的所有行为
      • 与普通函数的区别是,内联函数会在适当的地方像宏一样展开,所以不需要函数调用的开销
    • 函数的声明和实现必须同时加 inline 才算内联函数

    • 在类内部的成员函数前,都隐式加了关键字 inline,也就是说,类内部的成员函数自动成为内联函数
  • 优点

    • 解决宏缺陷,本身是一个函数,带来宏优势,以空间换时间 (内联函数占用空间,但是省去了函数调用时候的压栈、跳转、返回的开销),在适当的时候展开
  • 内联处理的例外情况

    某些特殊情况下,写了关键字 inline 也不会按照内联方式处理。

    • 不能存在任何形式的循环语句
    • 不能存在过多的条件判断语句
    • 函数体不能过于庞大
    • 不能对函数进行取地址操作
  • 注意事项
    • 内联函数只是给编译器一个建议,编译器不一定会接受这种建议
    • 有时候编译器会自动将普通函数做内联编译

函数的默认参数和占位参数

  • 默认参数
    • 在形参中指定默认值
    • 如果有一个位置有了默认参数,那么从这个位置起,从左到右都必须有默认值
    • 函数的声明和实现,只能有一个提供默认参数,不可以同时添加默认参数
  • 占位参数
    • 只写一个类型进行占位,调用时必须传入占位值
    • 占位参数也可以有默认值
  • 示例

    “`c++

    <h1>define _CRT_SECURE_NO_WARNINGS</h1>

    <h1>include</h1>

    using namespace std;

    // 1.默认参数 -> 在形参中指定默认值
    // 如果有一个位置有了默认参数,那么从这个位置起,从左到右都必须有默认值
    int func1(int a, int b = 20, int c = 30)
    {
    return a + b + c;
    }

    // 函数的声明和实现,只能有一个提供默认参数,不可以同时添加默认参数
    void func2(int a = 10, int b = 20);
    //void func2(int a = 10, int b = 20) {} // Error! 重定义默认参数
    void func2(int a, int b){}

    // 2.占位参数
    void func3(int a, int) // 函数内部无法使用占位参数
    {}

    void func4(int a, int b, int = 30) // 占位参数也可以设置默认值

    {}

    int main(void)
    {
    cout << func1(10) << endl; // 60
    func2();

    <pre><code>// 带占位参数的函数调用
    //func3(10); // Error! 错误调用,占位参数也是参数,必须传参
    func3(10, 20);
    func4(10, 20);

    system("pause");
    return EXIT_SUCCESS;
    </code></pre>

    }

    “`

函数重载

  • 条件

    • 在同一个作用域,函数名称相同,但是函数参数的个数、类型、顺序不同
    • 返回值不可以作为函数重载的条件
      • 因为我们调用的时候可能会忽略返回值,例如:func();,而不是 int a = func();
  • 注意事项
    • 函数重载中,引用两个版本
      • 避免二义性
      • 加 const 和不加 const 的引用可以作为
    • 函数重载碰到默认参数,也要避免二义性

  • 函数重载实现原理

    • 原理
      • 在 C++ 中有函数重载,会修饰函数名 (这个修饰没有统一标准),如果根据名称修饰方式来查找这个函数,那么就会发生链接错误,导致调用 C 语言函数失败
    • 报错
      • 一个无法解析的外部命令
    • 解决方法
      • 在 C++ 代码中添加代码 extern “C”

        “`c++

        <h1>define _CRT_SECURE_NO_WARNINGS</h1>

        <h1>include</h1>

        <h1>include "test.h"</h1>

        using namespace std;

        //extern "C" void test(); // 已废弃

        “`

        • 告诉编译器,show 函数用 C 语言的方式
        • 很少用,太麻烦
        • 这种方法可能已经废弃了,会报错
          • 链接规范与 “test” 的早期规范冲突
      • 在 C 语言的头文件中添加代码 extern “C”

        “`c++

        <h1>ifdef __cplusplus</h1>

        extern "C" {

        <h1>endif</h1>

        void test(); // 中间放函数声明

        <h1>ifdef __cplusplus</h1>

        }

        <h1>endif</h1>

        “`

  • 代码

英仔
版权声明:本站原创文章,由 英仔 2022-08-15发表,共计12765字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)