Gamer's Show

你知道人生最要紧的事是快乐不停

0%

L3-封装与接口

函数重载&缺省值

函数重载

同名函数有多个不同函数来实现

  • 编译器通过输入值来决定所调用的函数,必须保证至少一个函数参数类型不同,其余不能作为区分标志;
  • 函数重载时,会优先调用类型匹配的函数实现,不存在时会进行类型转换

函数参数的缺省值

函数定义时设置默认值就是缺省值,在实参缺少时,编译会自动设置为default值

  • 有缺省值的函数参数,必须排在没有缺省值参数的后面
    1
    void print(char* name,int score,char* msg="pass")
  • 缺省值可能会导致函数调用二义性,造成冲突。此时编译器会拒绝代码:
    1
    2
    3
    4
    5
    6
    7
    8
    void fun(int a, int b=1) {
    cout << a + b << endl;
    }
    void fun(int a) {
    cout << a << endl;
    }
    //测试代码
    fun(2);//编译器不知道该调用第一个还是第二个函数

auto+deltype

auto

std=c++11编译,由编译器根据上下文自动确定变量的类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
auto x = 1;
// vector的遍历
// vector<string> a{"0", "1", "2", "3", "4", "5", "6", "7", "8"};
auto it = a.begin(); // 返回一个迭代器类型,一般来说我们并不关心迭代器具体的数据类型
while(it != a.end())
{
cout << *it << " ";
it++;
}
return 0;
}
// 运行结果 //
0 1 2 3 4 5 6 7 8
  1. 跟踪返回类型的函数:可以将函数返回类型的声明信息放到函数参数列表的后边进行声明,追踪返回类型在原本函数返回值的位置使用auto:
    1
    auto func(char* ptr,int val) -> int;
  2. 使用注意:
    • 必须可以在编译期确定其类型
    • 变量定义时必须初始化,两种错误如下:
      1
      2
      auto a; //错误
      auto b4 = 10, b5 = 20.0, b6 = 'a';//错误,没有推导为同一类型
    • 函数形参不能被声明为auto
    • auto并不是一个真正的类型,因此不能使用以类型为操作数的操作符
  • 代替冗长复杂、变量使用范围专一的变量声明,尤其是在循环
  • 定义模板函数时,用于声明依赖模板参数的变量类型。比如说函数A的两个特殊参数会传至模板参数B,B中对于形参的计算中所用到的中间参数或者结果等可以使用auto来定义,就使得模板参数可以在函数重载时具有普适性。同时也可以自动追踪返回类型。

decltype

可以对变量或表达式结果的类型进行推导,decltype(var)返回的是var的类型:

1
2
3
4
5
6
7
8
9
struct { char name[17]; } anon_u;
struct {
int d;
decltype(anon_u) id;
} anon_s[100]; // 匿名的struct数组
int main() {
decltype(anon_s) as; // 注意变量as的类型:数组
cin >> as[0].id.name;
...}

auto+deltype

自动追踪返回类型

  • C++11中推导返回类型
    1
    2
    auto func(int x, int y) -> decltype(x+y)
    {return x+y;}
  • C++14中返回类型可以不显式指定
    1
    2
    auto func(int x, int y)
    {return x+y;}

基础知识

内存申请与释放

  • new&delete:指针变量所指内存的动态生成和删除:
    1
    2
    3
    int * ptr = new int(10); // 单个变量
    int * array = new int[10]; // 10元素数组
    delete ptr; // 删除指针变量所指单个内存单元delete[] array; // 删除多个单元组成的内存

零指针

0NULLnullptr

  • 0NULL可以说是等价的,当int和ptr产生函数重载时的冲突时需要使用nullptr,这是一个严格意义上的空指针

基于范围的for循环

在循环头的圆括号中,由冒号”:”分为两部分,第一部分是用于迭代的变量,第二部分则表示将被迭代的范围。常见于容器的元素循环。

1
2
3
4
5
6
7
8
#include <iostream>
using namespace std;
int main() {
int arr[3] = {1, 3, 9};
for (int e : arr) // auto e:arr 也可以
cout << e << endl;
return 0;
}

类与对象

基本语法

在头文件中声明类

1
2
3
4
5
6
7
8
9
// matrix.h
#ifndef MATRIX_H
#define MATRIX_H
class Matrix {
int data[6][6];
public:
void fill(char dir);
};
#endif

成员变量与成员函数

通常,类的声明放在头文件中,而类的成员函数实现/定义放在实现文件中

  • 为了便于管理和复用,一般将不同的类分别保存为不同的头文件和实现文件

成员函数的两种定义方式

为了方便解决依赖关系,复杂的成员函数声明和定义一般是分离的,不适用类内定义

  • 类内定义
    1
    2
    3
    4
    5
    6
    class Matrix {
    public:
    void fill(char dir) {
    ...; //在类内定义成员函数
    }
    ...};
  • 类外定义
    1
    2
    3
    4
    5
    // matrix.cpp
    #include "matrix.h"
    void Matrix::fill(char dir) //类外需要类名限定
    {
    ...// 函数实现}

类成员的访问权限:private & public

类的成员(包括数据、函数)可以根据需要分成组,不同组设置不同的访问权限

  • public修饰的成员可以在类外访问
  • private默认权限,不允许在类外访问
    • class中成员的缺省属性为private,因而以下代码中data是默认权限:
      1
      2
      3
      4
      5
      class Matrix{
      int data;
      public:
      void fill(char dir)
      }
  • protected

用户定义类型:类class

  • 对象:用类定义的变量
  • 对象名.成员名or对象指针->成员名使用数据成员/函数,类外使用仅能访问public

this指针

所有成员函数的参数中,隐含着一个指向当前对象的指针变量,这也是成员函数与普通函数的重要区别。可以用作前“对象指针”访问/赋值

内联函数inline

函数调用需要压栈、跳转、退栈、返回,是一个比较慢的过程,大量调用函数会拖慢程序

  • 内联函数可以执行类型检查,进行编译器错误检查
  • 可以调试
    • debug版本没有真正内联,等同于一般函数,因而可以被调试
    • release版本实现内联,增加执行效率
  • 内联函数生成的是和函数等价的表达式

使用注意事项

  • 避免大段代码使用(相当于把所有调用处copy一遍)
  • 避免循环与复杂控制函数:优化问题
  • 避免声明与定义分离:编译时需要得到实现,需要写在同文件
  • 类声明中定义的函数认为是内联函数:构造函数、析构函数
  • 是建议
    • 编译器可以拒绝不值得内联的请求(忽略内联修饰符)
    • 会选择短小却没有内联修饰符的函数自行判断是否转化为内联函数