C++11 语言特性3: final "关键字" 与 模板模式

74 阅读4分钟

C++11 语言特性3: final 关键字 与 模板模式

1.基本用途

  • 修饰类: 该类不能被继承, 即不能派生出子类
  • 修饰类成员虚函数: 让其不能被子类重写

视频: www.bilibili.com/video/BV1ei…

2.语法

一般放到 类型名后面方法名与函数体中间

修饰类

struct A { };

// 情况1
struct B final { };
// 情况2
struct C final : public A { };

修饰类成员虚函数

struct A {
    // 情况1
    virtual void func1() final { }
    virtual void func2() = 0;
};

struct C final : public A {
    // 情况2
    void func2() final override { }
    //void func2() override final { }
};

3. 使用场景 - 现象 与 模板模式(设计模式)

3.1 基本场景及限制

对类的限制

struct A { };

struct B final { };

struct C final : public A { };

// error
struct D : public B { }
struct E : public C { }

上述中 B 和 C 都被 final 关键字修饰了而A别没有被修饰, 类型C能正常的继承A

但当尝试从类型B和C 进行派生出子类D 和 E 的时候, 编译器就会报错说不能继承final修饰的类(从其派生)

03-final_keyward.cpp:18:8: error: cannot derive from ‘final’ base ‘B’ in derived type ‘D’
   18 | struct D : public B { };
      |        ^
03-final_keyward.cpp:19:8: error: cannot derive from ‘final’ base ‘C’ in derived type ‘E’
   19 | struct E : public C { };
      | 

修饰类成员虚函数

struct A {
    virtual void func1() final { }
    virtual void func2() = 0;
};

struct C final : public A {
    void func1() override { } // error
    void func2() final override { }
};

C中可以正常重写(override)基类A中的接口func2 - 虚函数. 而由于func1被 final 关键字修饰后, 在子类C中尝试重写时, 编译器就会报 C::func1 重写 A::func1 的错误

03-final_keyward.cpp:13:10: error: virtual function ‘virtual void C::func1()’ overriding final function
   13 |     void func1() override { }
      |          ^~~~~
03-final_keyward.cpp:6:18: note: overridden function is ‘virtual void A::func1()’
    6 |     virtual void func1() final { }

3.2 多格式音频文件播放 - 接口设计之模板模式

接口设计者 - 至上而下的设计方法

播放流程抽象

播放一个音频文件, 不同的格式有不同的封装格式和编码格式, 大概的一个流程: 读取文件 -> 解决封装 -> 获取音频参数和数据 -> 解码(如果数据有编码的话) -> 配置音频参数 -> 播放 -> 播放结束并释放资源

我们可以简单的把这些流程抽象到 四个过程即:

  • 初始化(init)
  • 配置(config)
  • 播放(play)
  • 释放(deinit)

cpp11-feature-3-final-keyward.1.png

接口设计 - 过程模板

使用 final 关键字 修饰play接口, 使得接口不能被子类重写, 从接口层固定播放的四个过程

每个过程的具体实现交给对应的子类里去实现

class AudioPlayInterface {
// .....
public:
    virtual void play() final {
        // ... common op
        _init();
        _config();
        _startPlay();
        _deinit();
        // ... common op
    }
protected:
    virtual void _init() = 0;
    virtual void _config() = 0;
    virtual void _startPlay() = 0;
    virtual void _deinit() = 0;
// .....
};

接口实现

不同格式的音频文件, 实现AudioPlayInterface里对应的 播放过程的接口

class WAVPlay final : public AudioPlayInterface {

public:
    WAVPlay() : AudioPlayInterface("WAV") {
        // ...
    }

protected:
    void _init() override {
        std::cout << "WAVPlay: _init" << std::endl;
    }

    void _config() override {
        std::cout << "WAVPlay: _config" << std::endl;
    }

    void _startPlay() override {
        std::cout << "WAVPlay: _startPlay" << std::endl;
    }

    void _deinit() override {
        std::cout << "WAVPlay: _deinit" << std::endl;
    }


};

class MP3Play final : public AudioPlayInterface {

public:
    MP3Play() : AudioPlayInterface("MP3") {
        // ...
    }

protected:
    void _init() override {
        std::cout << "MP3Play: _init" << std::endl;
    }

    void _config() override {
        std::cout << "MP3Play: _config" << std::endl;
    }

    void _startPlay() override {
        std::cout << "MP3Play: _startPlay" << std::endl;
    }

    void _deinit() override {
        std::cout << "MP3Play: _deinit" << std::endl;
    }
};

4.测试源码 及 运行结果

测试源码

// g++ -std=c++11  03-final_keyward.cpp


// 基本语法
struct A {
    virtual void func1() final { }
    virtual void func2() = 0;
};

struct B final { };

struct C final : public A {
    // void func1() override { } // error
    void func2() final override { }
    //void func2() override final { }
};

// error
//struct D : public B { };
//struct E : public C { };

#include <iostream>
#include <string>

class AudioPlayInterface {
protected:
    AudioPlayInterface(std::string format) : _mFormat { format } { }

public:
    virtual void play() final {
        std::cout << "AudioPlayInterface: start -> " << _mFormat  << std::endl;
        _init();
        _config();
        _startPlay();
        _deinit();
        std::cout << "AudioPlayInterface: end..." << std::endl << std::endl;
    }
protected:
    virtual void _init() = 0;
    virtual void _config() = 0;
    virtual void _startPlay() = 0;
    virtual void _deinit() = 0;

protected:
    std::string _mFormat;
    // other data
};

class WAVPlay final : public AudioPlayInterface {

public:
    WAVPlay() : AudioPlayInterface("WAV") {
        // ...
    }

protected:
    void _init() override {
        std::cout << "WAVPlay: _init" << std::endl;
    }

    void _config() override {
        std::cout << "WAVPlay: _config" << std::endl;
    }

    void _startPlay() override {
        std::cout << "WAVPlay: _startPlay" << std::endl;
    }

    void _deinit() override {
        std::cout << "WAVPlay: _deinit" << std::endl;
    }


};

class MP3Play final : public AudioPlayInterface {

public:
    MP3Play() : AudioPlayInterface("MP3") {
        // ...
    }

protected:
    void _init() override {
        std::cout << "MP3Play: _init" << std::endl;
    }

    void _config() override {
        std::cout << "MP3Play: _config" << std::endl;
    }

    void _startPlay() override {
        std::cout << "MP3Play: _startPlay" << std::endl;
    }

    void _deinit() override {
        std::cout << "MP3Play: _deinit" << std::endl;
    }
};

int main() {

    AudioPlayInterface *wav = new WAVPlay();
    AudioPlayInterface *mp3 = new MP3Play();

    wav->play();
    mp3->play();

    delete wav;
    delete mp3;

    return 0;
}

运行结果

cpp11-feature-3-final-keyward.2.png

5.Other

cppreference-final

HelloWorld 开源项目 - 欢迎Star

image.png