C++ 高阶基础

指针 和 函数 (By Jabin)

  • C++ Primer Plus 中文 第六版
  • Essential C++ 中文版
  • 深度探索 C++ 对象模型
  • C++ 程序设计语言(1-3)(4) 第四版 英文版
  • A Tour of C++ (Second Edition)
  • Professional C++ (4th Edition)
  • 指针

    int* p; int* 是一个指针类型 定义了一个int* 类型的指针

    int *p; *p是一个int类型的值

    int n = 4;
    int *p = &n
    

    声明语句中初始化指针,被初始化的是指针,而不是指向它的值。

    c++中创建指针,计算机将分配用来存储地址的内存,不会分配用来存储指针所指向的数据的内存***

  • * 与 ++

    前缀递增(++)、递减(--)与 * 优先级相同,以从右到左的方式进行结合
    后缀递增、递减优先级相同,但比前缀递增、递减的优先级高,以从左往右的方式结合

    double arr[5] = {1.1, 2.2, 3.3, 4.4, 5.5};
    double *ptr = arr;
    ++ptr; //point to arr[1]
    *++ptr; //先++后*取值 即指针指向arr[1],再取arr[1]的值,最后ptr指向不变 结果为2.2
    ++*ptr; //先取arr[0]的值,再对值进行++操作,最后ptr指向不变,结果为1.1 + 1 = 2.1
    (*ptr)++; //带括号优先级高,先取arr[1]的值,再进行++操作,++后置所以结果为 1.1,指针*ptr指向2.1
    *ptr++; //先ptr++,再取值,同样因为++后置所以结果为1.1,指针*ptr位置+1,指向2.2
    
    
  • 函数转换

    class Page{
    public:
        Page();
        Page(int n); //前面加explicit 禁止隐式转换
        operator int() const;
    }
    
    //usage:
    Page page(10);  //int to Page
    or 
    Page page;
    page = 10;
    
    int n = page; // n = 10;
    
  • 返回对象

    Test GetOne(const Test & t1, const Test t2)
    {
      if(true) return t1;
      return t2;
    }
    
    const Test & GetOne(const Test & t1, const Test & t2)
    {
        if(true) return t1;
        return t2;
    }
    
    //Note:
    //1. 返回对象会调用复制构造函数,返回引用不会,第二个效率高
    //2. 引用指向的对象应该在调用函数执行时存在
    //3. t1 和 t2 都声明为const引用,所以返回类型是const才匹配
    

    如果返回的对象是被调用函数中的局部变量,则应返回对象,而不是引用,因为函数执行完毕时,局部引用对象将被析构回收

    Point Point::Operator+(const Point & p1, const Point & p2)
    {
      return Point(p1.x + p2.x, p1.y + p2.y);
    }
    
  • 模板函数

    模板函数(function template)参数列表通常由两种类型构成:

    1. 明确的类型

    2. 暂缓决定的类型

    template  void Display(string & msg, const vector * vec)
    
    template  T CalScore(T t1, T t2);
    
    template auto Sum(T1 t11, T2 t22) -> decltype(t11 + t22);
    
    //Usage: 用以上两个函数进行求和运算
    int a = 5;
    double b = 6.0;
    ::CalScore(a, b); //两个类型不一样,将报错;更改如下
    ::CalScore(a, b); //Right
    
    //decltype (t1 + t2) t; //make t type the same with (t1 + t2)
    ::Sum(a, b); //因为t11 和 t22 类型不明 所以使用decltype关键字声明返回类型。因为auto可以自动推断t11 + t22 类型,所以也可以去掉 decltype 声明
    
    
  • inline函数

    内联函数的编译代码与其他程序代码 “内联” 起来了。 也就是说,编译器将使用相应的函数代码替换函数调用。程序无需跳到另一个位置处执行代码,再跳回。因此,运行速度稍快,但是占用更多内存。

    声明一个 inline 函数,只需在函数前加关键字 inline 即可,如:

    inline int Sum(int a, int b);
    
    • 将函数声明为 inline 函数,只是对编译器提出要求,编译器是否执行我们的要求,还需视编译器而定。

    • 那些体积小,常被调用,从事计算量不复杂的函数常被声明为 inline 函数

  • 虚函数

    ​ 在函数前关键字 virtual 定义函数为虚函数。

    如果方法是通过引用或者指针调用的,没有关键字 virtual 时,程序将根据引用的类型或者指针类型选择方法;如果有关键字virtual,程序将根据引用或指针指向的对象类型来选择方法。

    ​ 通常编译器处理虚函数会给每个对象添加一个隐藏成员。隐藏成员中保存了一个指向函数地址数组的指针。这种数组称为虚函数表(virtual functions table, vtbl)。虚函数表中存储了为类对象进行声明的虚函数的地址。调用虚函数时,程序将查看存储在对象中的vtbl地址,然后转向相应的函数地址表。

    ​ 使用虚函数在内存和执行速度方面有一定成本:

    • 每个对象将增大,增大量为存储空间
    • 对于每个类,编译器都会创建一个地址表
    • 对于每个函数调用,都需需要到 vtbl 中查找地址

    通常给基类提供一个虚析构函数,即使并不需要

    Animal.h
    
    class Animal
    {
    private:
      std::string name;
      int legs;
    public:
      Animal(const std::string& name, const int& legs);
      std::string GetName() const;
      int GetLegs() const;
      virtual void ShowInfo();
    };
    
    
    Horse.h
    
    class Horse : public Animal
    {
    private:
      int height;
    public:
      Horse(const std::string& name, const int& legs, const int& height);
      virtual void ShowInfo(); //void ShowInfo() override
    };
    
    Animal.cpp
    
    void Animal::ShowInfo()
    {
      std::cout << "Animal's name: " << name << ", it has " << legs << " legs" << std::endl;
    }
    
    
    Horse.cpp
    
    Horse::Horse(const std::string& name,const int& legs,const int& height)
    : Animal(name, legs)
    {
      this->height = height;
    }
    
    void Horse::ShowInfo()
    {
      std::cout << "Animal's name: "<height << std::endl;
    }
    
    • Usage
      
      Animal animal("sheep", 4);
      Horse horse("horse", 4, 1);
      animal.ShowInfo();
      horse.ShowInfo();
    
      Animal& an = animal;
      Animal& ho = horse;
      an.ShowInfo();
      ho.ShowInfo();
    
      Animal* ani = &animal;
      Animal* hor = &horse;
      ani->ShowInfo();
      hor->ShowInfo();
    
    
    • virtual void ShowInfo()
    Animal's name: sheep, it has 4 legs
    Animal's name: horse, it has 4 legs, it's height: 1
    Animal's name: sheep, it has 4 legs
    Animal's name: horse, it has 4 legs, it's height: 1
    Animal's name: sheep, it has 4 legs
    Animal's name: horse, it has 4 legs, it's height: 1
    
    • void ShowInfo() 去掉 virtual 时
    Animal's name: sheep, it has 4 legs
    Animal's name: horse, it has 4 legs, it's height: 1
    Animal's name: sheep, it has 4 legs
    Animal's name: horse, it has 4 legs
    Animal's name: sheep, it has 4 legs
    Animal's name: horse, it has 4 legs
    

    如果要重新定义继承方法,要确保与原型完全相同。像 ShowInfo(Animal &animal) 将会覆盖 ShowInfo(). 如果返回类型是基类引用或指针,则可修改为指向派生类的引用或指针(新出现的特例)。这种特性称为 返回型协变(covariance of return type), 因为允许返回类型随类型变化而变化。如果不与原型相同,则原有函数将被隐藏,派生类对象无法访问。

  • 虚析构函数

    virtual ~Animal() = default
    
    • 使用虚析构函数可以确保正确的析构函数被调用
  • 静态/动态联编

    ​ 将源代码中函数调用解释为执行特定的函数代码块称为函数名联编(binding)。

你可能感兴趣的:(C++ 高阶基础)