音视频开发(十九):运算符重载、继承、多态、模版

目录

  1. 类和对象的重要知识点

  2. 运算符重载

  3. 继承

  4. 多态

  5. 模版

一、类和对象的重要知识点

1.1 深拷贝与浅拷贝

浅拷贝:简单的赋值拷贝操作,拷贝构造
深拷贝:在堆区重新申请空间,进行拷贝操作

1.2 this指针

this指针指向被调用的成员函数所属的对象。
本质是指针常量(即指针的指向是不可以修改的,但指向的内容的值是可以修改的)

用途

  1. 当形参和成员变量同名时,可用this指针来区分

  2. 在类的非静态成员函数中返回对象本身,可使用 return * this 返回该对象的引用,达到链式编程

1.3 const修饰成员函数

常函数:

  1. 成员函数后加const,称这个函数为常函数,本质上是 修饰this指针指向,让指针指向的内容值也不可以修改. eg: 返回值 函数名() const

  2. 一般常函数内不可以修改成员属性

  3. 成员属性声明时加关键字mutable后,在常函数中依然可以修改

常对象:

  1. 声明对象前加const,称该对象为常对象 eg: const Object obj;

  2. 常对象只能调用常函数。

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

实践

#include <iostream>

using namespace std;

class temp{

public:

   temp(){
       this->k = 10;

   }
//    void setValue(int k) const{ //函数后面加上const表示该函数中不可以修改成员变量的值
//          this->k = k;
//      }

    void setValue(int k) const{ //但是变量申明伟mutable 可以改变
         this->k = k;
     }

     int getValue() const{
         return this->k;
     }

private:
   mutable int k;
};


int main(void) {
    
    temp t;
    temp *p = &t;
    cout<< "t.getValue()="<<t.getValue()<<endl;//“.”是成员运算符,用于调取成员变量和成员函数
    cout<< "p->getValue()="<<p->getValue()<<endl;//“->”是地址运算符,用于引用成员变量和成员函数;

    return 0;
}

1.4 友元

目的是让一个函数或者类,访问另一个类中私有成员
友元用关键字 friend表示
友元的三种实现

  1. 全局函数做友元

  2. 类做友元

  3. 成员函数做友元

实践

#include <iostream>

using namespace std;

class temp{

friend int main(void) ;//声明该函数为友元函数

public:

   temp(){
       this->k = 10;
   }

   int getValue() const{
       return this->k;
   }


private:
    void setValue(int k) { 
        this->k = k;
    }
    int k;
};


int main(void) {
    
    temp t;
    cout<< "t.getValue()="<<t.getValue()<<endl;

    t.setValue(100);// 由于setValue是私有函数,如果正常情况类外部是无法访问的,通过在类中声明该函数为友元函数访问
    cout<< "t.getValue()="<<t.getValue()<<endl;
    return 0;
}

二、运算符重载

operator@
运算符重载(operator overloading)只是一种“语法上的方便”,它只是另一种函数调用的方式,本质上是函数(成员函数或者全局函数),简化省略operator后就是运算符重载的语法糖效果。

运算符重载有两种方式:

  1. 成员函数

  2. 全局函数
    运算符重载 也可以发生函数重载
    对于内置的数据的类型的运算符无法改变,只可用于用户自定义类型。
    不要滥用运算符重载
    常用的运算符重载 “+”、“<<”、“++”、“=” “< > != ==”

下面以重载”+”运算符,实现两个对象相加。

#include <iostream>

using namespace std;

class temp{

public:
   temp(){
       this->k = 10;
       this->t = 100;
   }

    int k;
    int t;
};

temp operator+(temp param1,temp param2){

   temp a;
   a.k = param1.k+param2.k;
   a.t = param1.t+param2.t;
   return a;
}

int main(void) {
    
    temp t1;
    temp t2;

    cout<<(t1+t2).k<<endl;
    cout<<(t1+t2).t<<endl;

    return 0;
}

三、继承

单继承时派生类的定义
语法

class 派生类名:继承方式 基类名
{
    成员声明;
}

多继承时派生类的定义
语法
class 派生类名:继承方式1 基类名1,继承方式2 基类名2,...
{
    成员声明;
}

有三种继承方式:共有继承;私有继承;保护继承

CPP允许多继承,但是实际开发中不建议使用多继承,因为父类中可能出现重名的属性或方法,需要加作用域区分。
菱形继承 可以采用虚函数虚继承的方式解决,即晚捆绑,这个我们在下一小节多态中再具体看。

通过sizeof查看父类和子类,父类的私有成员在子类中仍然占用存储空间,只不过不可以直接访问。

四、多态

多态性(Polymorphism)提供了接口与具体实现之间额一层隔离,改善了代码的组织结构和可读性,利于前后期的维护及扩展

动态多态满足条件

  1. 有继承关系

  2. 子类重写父类的虚函数

动态多态的场景:当父类的指针或者引用指向子类对象时,就发生了动态多态。

地址早绑定,在编译阶段确定了函数地址。
使用visual虚函数关键字告诉编译器,这个函数地址不能提前绑定,需要在运行阶段进行绑定,

加入virtual后编译器会分配一个指针 vfptr:virtual function pointer
vftable:virtual function table 记录虚函数地址。

当子类重写父类的虚函数
子类的虚函数表内部会替换成子类的虚函数地址

函数调用捆绑

把函数体与函数调用相联系称为捆绑(binding)。当捆绑在程序运行之前(有编译器和连接器)完成时,成为早捆绑(early biding)。在运行时才能确定捆绑类型成为晚捆绑(late dbinding)。
对于特定的函数,为了引起晚捆绑,CPP要求在基类中声明这个函数时使用virtual关键字。为了创建一个virtual成员函数,可以简单地在这个函数声明的前面加上关键字virtual,仅仅在声明的使用需要使用关键值virtual,定义时不需要。如果一个函数在基类中被声明为virtual,那么所有的派生类中它都是virtual。

纯虚函数与抽象类
在设计时,比较已扩展性的设计时基类仅仅作为其派生类的一个接口,音视频开发知识点路线图:https://docs.qq.com/doc/DQm1VTHBlQmdmTlN2,即不希望用户创建一个基类对象。要做到这一点,可以在基类中加入至少一个纯虚函数(pure virtual function),使基类变成抽象(abstract)类。纯虚函数使用关键字virtual,并且在函数后面加上 =0. 相当于java的 abstract前缀

纯虚函数的语法
virtual void f() = 0;

  1. 抽象类无法实例化对象 和java一致。

  2. 抽象类的子类必须重写父类的纯虚函数 —》和java一致

构造函数不能为虚函数,但析构函数能够且常常必须使虚函数

虚析构和纯虚析构
问题:父类指针在析构时,不会调用子类的析构函数
利用虚析构可以解决这个问题。
纯虚析构 既要声明,也要实现。

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

实践

#include<stdio.h>
#include<iostream>
using namespace std;

class BaseClass 
{
public:
    BaseClass(){
        cout<< " BaseClass construct"<<endl;
    }
    //添加virtual关键字,告诉编译器,采用晚捆绑的方式,编译后会创建一个vfprt,指向vftable,存放print的地址
   virtual void print(){
        cout << " BaseClass print"<<endl;
    }

    ~BaseClass(){
        cout<< " BaseClass destruct"<<endl;
    }
private:
    void innerMethod(){
        cout<< " BaseClass inner"<<endl;
    }
};

class BaseClass2 
{
public:
    BaseClass2(){
        cout<< " BaseClass2 construct"<<endl;
    }
   virtual void print(){
        cout << " BaseClass2 print"<<endl;
    }

    ~BaseClass2(){
        cout<< " BaseClass2 destruct"<<endl;
    }
private:
    void innerMethod(){
        cout<< " BaseClass2 inner"<<endl;
    }
};

class ChildClass : public BaseClass, private BaseClass2
{
public:
    ChildClass(){
        cout<< " ChildClass construct"<<endl;
    }
    void print()
    {
        BaseClass::print();
        // BaseClass::innerMethod(); //不能访问父类的私有函数或变量
        BaseClass2::print(); //私有继承,可以访问父类的public函数
        cout<< "ChildClass print"<<endl;
    }
     ~ChildClass(){
        cout<< " ChildClass destruct"<<endl;

    }
};

int main()
{
    BaseClass tmp;
    ChildClass childClass;
    //如果BaseClas中print设置virtual后,编译器分配一个指针vfptr,派生类也会继承该vfptr
    cout<<"size of BaseClass"<<sizeof(tmp)<<endl;
    cout<<"size of childClass"<<sizeof(childClass)<<endl;

    BaseClass *baseclass = &childClass; //创建BaseClass类 指向 childClass

    baseclass->print();//如果print不加virtual,此时打印出的只有BaseClass的print
    return 0;
}

五、模版

继承和组合提供了重用对象代码的方法,而CPP的模版特征提供了重用源代码的方法,建立通用性的模版,大大提高复用性。

有点像工具类中的通用方法,但是有不仅限于此,通过泛型提供了更好的通用性,另外不仅有函数模版,还有类模版,下面我们一起来学习具体知识点:

特点:

  1. 模版只是一个框架,不能直接使用

  2. 模版不是万能的

模版的声明和定义通常放在头文件中,这看似违背了“头文件不要放置分配内存存储空间的任何东西”,但模版的定义比较特殊, 在template<…>之后的代码意味着编译器在当时不分配存储空间,等到被使用时才分配。

5.1 函数模版

使用场景:泛型编程

语法
template<typename T>
函数声明或者定义

template --》声明创建模版
typename -->表示其后面的符号是一种数据类型,也可以用class替代
T --》通用的数据类型,名称可以替换,通常为大写字母

使用函数模版的方式
自动类型推导 —〉必须要推导
显示指定类型

函数注意事项
自动类型推导,必须推导出一致的数据类型T才可以使用。
模版必须要确定T的数据类型,才可以使用

普通函数与函数模版的区别
是否可以发生自动类型转换(隐式类型转换)

普遍函数可以,函数模版自动推导不可以,显示指定可以。
建议使用显示指定类型的方式调用函数模版

普通函数与函数模版的调用规则

  1. 如果函数模版和普通函数都可以实现,优先调用普通函数

  2. 可以通过空模版参数列表来强制调用函数模版

  3. 函数模版也可以发生重载

  4. 如果函数模版可以产生更好的匹配,优先调用函数模版
    即然提供了函数模版,最好就不要提供普通函数,否则容易出现二义性。

实践

#include<stdio.h>
#include<iostream>
#include<string>
using namespace std;

template<typename T>
int add(T a,T b){
    cout <<"template T method"<<endl;
    return a + b;
}

template<typename T, typename S>
int add(T a,S b){
    cout <<"template S T method"<<endl;
    return a + b;
}

int add(int a,int b){
    cout <<"normal method"<<endl;
    return a + b;
}

int main()
{

    cout <<"add "<< add(1,1)<< endl; //如果函数模版和普通函数都可以实现,优先调用普通函数
    cout <<"add "<< add<>(1,1)<< endl;//可以通过空模版参数,强制的调用模版函数
    cout <<"add "<< add(1.0,2.0)<< endl;
    cout <<"add "<< add(1.0,3)<< endl; //模版支持重载
    cout <<"add "<< add<int ,int>(1.0,3)<< endl; 
    // cout <<"add "<< add("ab","ca")<< endl; // 错误,模版不是万能的,复合对类型才行
  
    return 0;
}

5.2 类模版

template<class T>
class

template --》声明创建模版
class -->表示其后面的符号是一种数据类型,当然也可以写为typename
T --》通用的数据类型,名称可以替换,通常为大写字母

类模版和函数模版的区别

  1. 类模版没有自动类型推导,只能使用显示指定类型

  2. 类模版在模版参数列表中可以有默认参数

类模版中成员函数的创建时机

  1. 普通类中的成员函数一开始就可以创建

  2. 类模版中的成员函数在调用时才可以创建

类模版对象做函数参数,三种方式

  1. 指定传入的类型 — 直接显示对象的数据类型

  2. 参数模版化 — 将对象中的参数变为模版进行传递

  3. 整个类模版化 — 将这个对象类型 模版化进行传递

实践

#include <iostream>

using namespace std;


template <class T>
class templateclass {
public:

   //构造函数
    templateclass(T k = 10) 
    { 
        this->k = k;
        cout<< "templateclass construt k = "<<this->k <<endl;
     }

     void setValue(T k){
         this->k = k;
     }

    T getVaule() const { return k; }

private:
    T k;
};


// 使用类模板,函数参数必须显示指定类型
void print(templateclass<int>& params) {
    cout << params.getVaule() << endl;
}

int main(void) {
    // templateclass<> p(100);//错误,类模版没有自动类型推导,只能使用显示指定类型
    templateclass<int> k(100);
    cout << k.getVaule() << endl;

    k.setValue(1000.5);//会被强制转为模版类声明的类型
    // k.setValue(“a”);//错误
    print(k);
    return 0;
}

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值