真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

跟我學c++中級篇——類型擦除-創(chuàng)新互聯(lián)

一、類型擦除

很多人一直都認為,類型擦除是一些高級語言(如Java)才具有的,其實在c++中也可以實現(xiàn)類型擦除。那么什么是類型擦除呢?我們都知道,C/c++是一門強類型語言,也就是說,編譯器必須知道數(shù)據(jù)是屬于什么類型的。說的直白一點,就是int類型還是其它什么類型,當然這些類型里也包括對象類型。而類型擦除,就是要把這些數(shù)據(jù)的類型抹去,或者說擦除掉,當然也可以理解為隱藏掉數(shù)據(jù)的類型。這不和剛剛說的強類型語言相反么,這樣做有什么用處呢?
做為強類型語言,所有的數(shù)據(jù)類型必須強調可知就會有一個顯示的問題,無法用一個通過的定義來描述這些數(shù)據(jù)類型非特定的行為。而往往是這些行為決定了設計上的解耦和分離。而這種行為的不同就是程序設計可擴展性和簡潔高效的前提和基礎,它可以顯著的降低程序中的侵入式設計。而在前面也反復提到過,c++從設計上本身是無法提供在運行時動態(tài)獲取數(shù)據(jù)對象的類型的功能的。這也讓類型擦除更具重要性。
這里需要提到一個定義:鴨子類型(英語:duck typing)是動態(tài)類型的一種風格。在這種風格中,一個對象有效的語義,不是由繼承自特定的類或實現(xiàn)特定的接口,而是由“當前方法和屬性的集合”決定。這玩意兒有啥用呢,它可以不通過繼承實現(xiàn)多態(tài)。

10年積累的網(wǎng)站建設、做網(wǎng)站經(jīng)驗,可以快速應對客戶對網(wǎng)站的新想法和需求。提供各種問題對應的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡服務。我雖然不認識你,你也不認識我。但先建設網(wǎng)站后付款的網(wǎng)站建設流程,更有敖漢免費網(wǎng)站建設讓你可以放心的選擇與我們合作。二、類型擦除的方式

那么數(shù)據(jù)類型有幾種方式呢,常見的有以下幾種:
1、多態(tài)
多態(tài)一般都很了解,可以通過父類型指針來管理子類型的指針,實現(xiàn)了對子對象類型的隱藏。但這種方式的缺點也非常明顯,需要寫繼承的類而且數(shù)據(jù)類型的隱藏并不全面,至少父類型始終要暴露出來。
2、模板
模板在前面也提到過多次,通過模板,可以實現(xiàn)對多種類型的行為操作進行抽象。比如一個模板的加法函數(shù)(比如多個類都調用此加法函數(shù)),可以實現(xiàn)對不同的數(shù)據(jù)類型的操作,如果對象也重載了加法,同樣也可以使用。不過,在基本類型中,這種模板函數(shù)抽象的行為就無能為力了。
3、容器
容器之所以可以擦除,其實更類似于模板,這個不用多說。另外還有一種組合容器,其實有些類似共用體。比如std::variant 。但此種方式其實也明確了數(shù)據(jù)的類型,只是多了幾種罷了,讓編譯器在真正使用時進行選擇。所以這種類型擦除更接近一種類型判斷。
4、通用類型
這個就得提到前面分析的std::any,通過它可以進行數(shù)據(jù)類型的動態(tài)轉換。但是其才應用時,仍然需要指定具體的類型后才能使用。
5、閉包
所謂閉包,其實就是Lambda表達式,通過它和std::function的配合,實現(xiàn)類型的擦除。

從上面的分析可以看到,其實具體實現(xiàn)上來看,如果想實現(xiàn)的比較理想(如any等的實現(xiàn)基本就是有限定范圍的),基本上都需要兩個基本條件,一個是帶約束的模板構造函數(shù),另外一個就是函數(shù)指針。再回頭和SBO系列相對比一下就可以明白為啥std::function的實現(xiàn)機制中的基本方式了。同時,也可以搞明白,其實類型擦除就是一種使用函數(shù)指針來實現(xiàn)多態(tài)的機制。

三、應用場景

類型擦除的應用場景非常多,特別是在設計中,如何實現(xiàn)一個非繼承方式來搞定擴展而勿需考慮數(shù)據(jù)類型時,都可以使用這種方式。當然,如果看過MFC實現(xiàn)的源碼,也可以使用類似其侵入式的設計來實現(xiàn),但那個太復雜了,而且可擴展性也有限。業(yè)界現(xiàn)在基本已經(jīng)不在采用那種方式來設計程序。
另外上一篇中SBO系列中,其實也是采用了這種方式來實現(xiàn)。只要能夠明白函數(shù)指針的機制,這種設計就可以不拘泥于某種場景,自由的發(fā)揮。而所謂類型擦除不過是其中一個應用的重要實現(xiàn)而已。

四、例程

這里的例程使用一個同學的例程,大家參考分析一下就明白了。

class MyFunction
{
private:
    class FunctorWrapper
    {
    public:
        virtual ~FunctorWrapper() = default;
        virtual FunctorWrapper* clone() const = 0;
        virtual void call() const = 0;
    };
    templateclass ConcreteWrapper : public FunctorWrapper
    {
    public:
        ConcreteWrapper(const T& functor)
            : functor(functor) { }
        virtual ~ConcreteWrapper() override = default;
        virtual ConcreteWrapper* clone() const
        {
            return new ConcreteWrapper(*this);
        }
        virtual void call() const override
        {
            functor();
        }
    private:
        T functor;
    };
public:
    MyFunction() = default;
    templateMyFunction(T&& functor)
        : ptr(new ConcreteWrapper(functor)) { }
    MyFunction(const MyFunction& other)
        : ptr(other.ptr->clone()) { }
    MyFunction& operator=(const MyFunction& other)
    {
        if (this != &other)
        {
            delete ptr;
            ptr = other.ptr->clone();
        }
        return *this;
    }
    MyFunction(MyFunction&& other) noexcept
        : ptr(std::exchange(other.ptr, nullptr)) { }
    MyFunction& operator=(MyFunction&& other) noexcept
    {
        if (this != &other)
        {
            delete ptr;
            ptr = std::exchange(other.ptr, nullptr);
        }
        return *this;
    }
    ~MyFunction()
    {
        delete ptr;
    }
    void operator()() const
    {
        if (ptr)
            ptr->call();
    }
    FunctorWrapper* ptr = nullptr;
};

再看一下不使用繼承的:

class MyFunction
{
private:
    static constexpr std::size_t size = 16;
    static_assert(size >= sizeof(void*), "");
    struct Data
    {
        Data() = default;
        char dont_use[size];
    } data;
    templatestatic void functorConstruct(Data& dst, T&& src)
    {
        using U = typename std::decay::type;
        if (sizeof(U)<= size)
            new ((U*)&dst) U(std::forward(src));
        else
            *(U**)&dst = new U(std::forward(src));
    }
    templatestatic void functorDestructor(Data& data)
    {
        using U = typename std::decay::type;
        if (sizeof(U)<= size)
            ((U*)&data)->~U();
        else
            delete *(U**)&data;
    }
    templatestatic void functorCopyCtor(Data& dst, const Data& src)
    {
        using U = typename std::decay::type;
        if (sizeof(U)<= size)
            new ((U*)&dst) U(*(const U*)&src);
        else
            *(U**)&dst = new U(**(const U**)&src);
    }
    templatestatic void functorMoveCtor(Data& dst, Data& src)
    {
        using U = typename std::decay::type;
        if (sizeof(U)<= size)
            new ((U*)&dst) U(*(const U*)&src);
        else
            *(U**)&dst = std::exchange(*(U**)&src, nullptr);
    }
    templatestatic void functorInvoke(const Data& data)
    {
        using U = typename std::decay::type;
        if (sizeof(U)<= size)
            (*(U*)&data)();
        else
            (**(U**)&data)();
    }
    templatestatic void (*const vtables[4])();
    void (*const* vtable)() = nullptr;
public:
    MyFunction() = default;
    templateMyFunction(T&& obj)
        : vtable(vtables)
    {
        functorConstruct(data, std::forward(obj));
    }
    MyFunction(const MyFunction& other)
        : vtable(other.vtable)
    {
        if (vtable)
            ((void (*)(Data&, const Data&))vtable[1])(this->data, other.data);
    }
    MyFunction& operator=(const MyFunction& other)
    {
        this->~MyFunction();
        vtable = other.vtable;
        new (this) MyFunction(other);
        return *this;
    }
    MyFunction(MyFunction&& other) noexcept
        : vtable(std::exchange(other.vtable, nullptr))
    {
        if (vtable)
            ((void (*)(Data&, Data&))vtable[2])(this->data, other.data);
    }
    MyFunction& operator=(MyFunction&& other) noexcept
    {
        this->~MyFunction();
        new (this) MyFunction(std::move(other));
        return *this;
    }
    ~MyFunction()
    {
        if (vtable)
            ((void (*)(Data&))vtable[0])(data);
    }
    void operator()() const
    {
        if (vtable)
            ((void (*)(const Data&))vtable[3])(this->data);
    }
};
 
templatevoid (*const MyFunction::vtables[4])() =
{
    (void (*)())MyFunction::functorDestructor,
    (void (*)())MyFunction::functorCopyCtor,
    (void (*)())MyFunction::functorMoveCtor,
    (void (*)())MyFunction::functorInvoke,
};

有一個可能約束的模板構造函數(shù)和函數(shù)指針。上面的封裝需要相關的模板類型實現(xiàn)了拷貝和仿函數(shù)的功能,否則不能使用。更多的可以參考一下原文:
https://www.cnblogs.com/jerry-fuyi/p/12664787.html#sbo
正所謂長江后浪推前浪,這位同學的博客質量相當高,大家可以看一看。
也可以參考:
https://quuxplusone.github.io/blog/2019/03/18/what-is-type-erasure/
https://zhuanlan.zhihu.com/p/351291649
https://zhuanlan.zhihu.com/p/351464404
https://zhuanlan.zhihu.com/p/374562057
一般來說,在一元類型操作中(換句話說就是函數(shù)中一般只有一個模板參數(shù))表現(xiàn)很良好,而有多個選項的話,則不太友好。

五、總結

現(xiàn)在大家都很惶惑,不知道未來的方向??赡芴а弁?,都是不可預知的迷茫。越是在這個時候兒,越是要沉住氣,要不斷的錘煉自己的技術,趁著這個機會把基礎打得更牢更扎實;同時,要不斷的尋找方向,不要因為找不到方向就焦慮,因為可能下一次就會發(fā)現(xiàn)正確的方向。沒有方向要大膽去找,有了方向要大膽去試。
特別是年青人,現(xiàn)在屬于你們的世界。努力揮灑汗水,光明就在拐角。

你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧


文章標題:跟我學c++中級篇——類型擦除-創(chuàng)新互聯(lián)
URL網(wǎng)址:http://weahome.cn/article/esosg.html

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部