99精品久久精品一区二区-亚洲熟妇无码?v在线播放-日本国产精品无码字幕在线观看-久久久亚洲永夜AV-亚洲一级无码一区二区一-免费国产成高清人在线视频-中文字幕乱码免费观看-国产毛片精品妇女久久久

ARTICLE DETAIL

資訊詳情

深耕商務(wù)建站與企業(yè)官網(wǎng)運(yùn)營(yíng)的一線實(shí)戰(zhàn)洞察。

C++模板類聲明與實(shí)現(xiàn)分離:三種策略深度解析與實(shí)戰(zhàn)

C++模板類聲明與實(shí)現(xiàn)分離:三種策略深度解析與實(shí)戰(zhàn) 1. 項(xiàng)目概述為什么要把模板類的“皮”和“肉”分開(kāi)如果你寫過(guò)C模板類尤其是稍微復(fù)雜一點(diǎn)的十有八九都遇到過(guò)那個(gè)經(jīng)典的鏈接錯(cuò)誤undefined reference to Stackint::push(int const)。編譯器在編譯用到Stackint的main.cpp時(shí)它看到了模板的聲明知道有push這個(gè)方法但就是找不到這個(gè)方法的“肉身”在哪。這感覺(jué)就像你拿到了一份功能強(qiáng)大的產(chǎn)品說(shuō)明書頭文件但關(guān)鍵的零部件實(shí)現(xiàn)代碼卻不在同一個(gè)包裹里工廠鏈接器自然沒(méi)法把產(chǎn)品組裝出來(lái)。這就是我們今天要徹底搞明白的核心問(wèn)題如何優(yōu)雅地定義C模板并把它的類定義聲明和類實(shí)現(xiàn)定義分離開(kāi)來(lái)。很多人包括一些有經(jīng)驗(yàn)的開(kāi)發(fā)者都習(xí)慣把模板的所有代碼一股腦塞進(jìn)頭文件.h或.hpp里。這當(dāng)然能工作但它帶來(lái)了幾個(gè)頭疼的問(wèn)題編譯時(shí)間爆炸式增長(zhǎng)因?yàn)槊總€(gè)包含該頭文件的.cpp文件都要重新實(shí)例化一遍模板、代碼結(jié)構(gòu)混亂聲明和實(shí)現(xiàn)攪在一起以及最關(guān)鍵的——它模糊了傳統(tǒng)C“聲明放.h實(shí)現(xiàn)放.cpp”的良好工程實(shí)踐。以我們最熟悉的“?!睘槔Y(jié)構(gòu)清晰操作明確push, pop, top, empty是理解模板分離機(jī)制的絕佳模型。通過(guò)實(shí)現(xiàn)一個(gè)分離的模板棧你不僅能掌握解決上述鏈接錯(cuò)誤的方法更能深入理解C模板的實(shí)例化機(jī)制、顯式實(shí)例化的威力以及如何組織大型模板庫(kù)的代碼結(jié)構(gòu)。這對(duì)于編寫可復(fù)用、易維護(hù)、編譯高效的C庫(kù)至關(guān)重要也是邁向高級(jí)C開(kāi)發(fā)的必經(jīng)之路。2. 核心原理模板的“兩次編譯”與分離困境要解決問(wèn)題得先理解問(wèn)題是怎么來(lái)的。C模板的工作機(jī)制和普通類有本質(zhì)區(qū)別這導(dǎo)致了它們“分家”特別困難。2.1 普通類的編譯鏈接模型對(duì)于普通類比如一個(gè)PlainStack非模板流程非常清晰聲明在頭文件(plainstack.h)這里只有類藍(lán)圖和方法原型。// plainstack.h class PlainStack { public: void push(int value); int top() const; // ... 其他聲明 private: int data[100]; int index; };實(shí)現(xiàn)在源文件(plainstack.cpp)這里給出方法的具體實(shí)現(xiàn)。// plainstack.cpp #include plainstack.h void PlainStack::push(int value) { data[index] value; } int PlainStack::top() const { return data[index-1]; }編譯階段編譯器分別編譯main.cpp和plainstack.cpp生成目標(biāo)文件.o。在編譯main.cpp時(shí)它只需要看到plainstack.h中的聲明知道push和top長(zhǎng)什么樣函數(shù)簽名至于它們具體怎么做編譯器暫時(shí)不關(guān)心。鏈接階段鏈接器登場(chǎng)它的任務(wù)就是把main.o里對(duì)PlainStack::push和PlainStack::top的“調(diào)用請(qǐng)求”與plainstack.o里這兩個(gè)函數(shù)的“實(shí)際地址”連接起來(lái)。一旦找到程序就能運(yùn)行。這個(gè)模型的核心是分離編譯接口和實(shí)現(xiàn)分離各自獨(dú)立編譯最后再由鏈接器組裝。這極大地提高了編譯效率修改實(shí)現(xiàn)文件只需重新編譯該文件本身。2.2 模板類的“兩次編譯”困境模板類StackT就完全不同了。它不是一個(gè)具體的類而是一個(gè)“類工廠”的配方。編譯器需要根據(jù)你使用的具體類型比如Stackint,Stackstd::string來(lái)現(xiàn)場(chǎng)生成對(duì)應(yīng)的類。這個(gè)過(guò)程叫做實(shí)例化。關(guān)鍵在于實(shí)例化發(fā)生在編譯階段而不是鏈接階段。當(dāng)編譯器在main.cpp中看到Stackint intStack;時(shí)它必須立刻知道如何生成Stackint這個(gè)具體類的所有方法push(int),pop(), 等等。如果這些方法的實(shí)現(xiàn)定義在另一個(gè).cpp文件里而編譯器在編譯main.cpp時(shí)沒(méi)有看到它們那它就沒(méi)辦法生成代碼。到了鏈接階段鏈接器發(fā)現(xiàn)main.o需要Stackint::push的代碼但翻遍所有.o文件都找不到——因?yàn)閟tack.cpp里只有模板的“配方”泛型代碼沒(méi)有生成任何具體的Stackint代碼。這就是“undefined reference”錯(cuò)誤的根源。所以傳統(tǒng)的.h聲明 .cpp實(shí)現(xiàn)模式對(duì)模板行不通因?yàn)閷?shí)現(xiàn)代碼在編譯main.cpp時(shí)不可見(jiàn)。注意這里常有一個(gè)誤解認(rèn)為“模板不能分離編譯”。更準(zhǔn)確的說(shuō)法是模板的聲明和定義必須在同一個(gè)翻譯單元通常就是一個(gè).cpp文件及其包含的所有頭文件中對(duì)編譯器可見(jiàn)才能成功實(shí)例化。我們接下來(lái)的所有技巧都是圍繞如何滿足這個(gè)條件而展開(kāi)的。3. 方案選型三種主流分離策略的深度對(duì)比既然知道了問(wèn)題的癥結(jié)在于“編譯器在需要的時(shí)候看不到實(shí)現(xiàn)”那么解決方案就是想辦法讓編譯器看到。主要有三種主流策略各有優(yōu)劣適用于不同場(chǎng)景。3.1 方案一包含模式 (The Inclusion Model) —— 最簡(jiǎn)單直接這是最常見(jiàn)、也是新手最該先掌握的方法。顧名思義就是把模板的實(shí)現(xiàn)也直接寫在頭文件里。具體做法 創(chuàng)建一個(gè)頭文件例如stack.hpp用.hpp后綴來(lái)暗示這是包含實(shí)現(xiàn)的模板頭文件是個(gè)好習(xí)慣里面同時(shí)包含類聲明和所有成員函數(shù)的定義。代碼結(jié)構(gòu)// stack.hpp #ifndef STACK_HPP #define STACK_HPP #include vector #include stdexcept // 用于 std::underflow_error template typename T class Stack { public: bool empty() const; void push(const T item); void pop(); T top(); const T top() const; // 提供const版本用于const對(duì)象 private: std::vectorT elems; }; // ---------- 成員函數(shù)定義實(shí)現(xiàn)直接跟在后面 ---------- template typename T bool StackT::empty() const { return elems.empty(); } template typename T void StackT::push(const T item) { elems.push_back(item); } template typename T void StackT::pop() { if (elems.empty()) { throw std::underflow_error(Stack::pop(): empty stack); } elems.pop_back(); } template typename T T StackT::top() { if (elems.empty()) { throw std::underflow_error(Stack::top(): empty stack); } return elems.back(); } template typename T const T StackT::top() const { // 重載const版本代碼邏輯相同但返回const引用 if (elems.empty()) { throw std::underflow_error(Stack::top() const: empty stack); } return elems.back(); } #endif // STACK_HPP使用方式在main.cpp中直接#include stack.hpp即可。優(yōu)點(diǎn)零學(xué)習(xí)成本絕對(duì)可靠完全符合C標(biāo)準(zhǔn)永遠(yuǎn)不會(huì)出現(xiàn)鏈接錯(cuò)誤。簡(jiǎn)單明了所有代碼都在一個(gè)文件里查看和修改都方便。缺點(diǎn)編譯時(shí)間慢這是最致命的缺點(diǎn)。如果這個(gè)模板頭文件被幾十個(gè).cpp文件包含那么模板代碼就會(huì)被編譯幾十次。如果模板實(shí)現(xiàn)很復(fù)雜比如一個(gè)龐大的矩陣運(yùn)算庫(kù)這將嚴(yán)重拖慢整個(gè)項(xiàng)目的編譯速度。暴露實(shí)現(xiàn)細(xì)節(jié)你不得不將所有的實(shí)現(xiàn)細(xì)節(jié)包括引用的其他頭文件、使用的內(nèi)部數(shù)據(jù)結(jié)構(gòu)暴露給用戶。這破壞了封裝性也使得用戶代碼可能因?yàn)槟愕膶?shí)現(xiàn)細(xì)節(jié)而意外編譯失敗例如你的實(shí)現(xiàn)里用了#include algorithm用戶代碼可能并不需要知道這個(gè)。適用場(chǎng)景小型項(xiàng)目、模板代碼量不大、或者對(duì)編譯時(shí)間不敏感的原型開(kāi)發(fā)階段。3.2 方案二顯式實(shí)例化 (Explicit Instantiation) —— 平衡之道這是真正實(shí)現(xiàn)“聲明與實(shí)現(xiàn)分離”的標(biāo)準(zhǔn)方法。我們回歸傳統(tǒng)的.h和.cpp文件結(jié)構(gòu)但在.cpp文件的末尾明確告訴編譯器“請(qǐng)為我生成這些特定類型的模板實(shí)例”。具體做法頭文件 (stack.h)只包含模板的聲明。// stack.h #ifndef STACK_H #define STACK_H #include vector #include stdexcept template typename T class Stack { public: bool empty() const; void push(const T item); void pop(); T top(); const T top() const; private: std::vectorT elems; }; #endif // STACK_H實(shí)現(xiàn)文件 (stack.cpp)包含成員函數(shù)的定義并在文件末尾進(jìn)行顯式實(shí)例化。// stack.cpp #include stack.h // 成員函數(shù)定義 template typename T bool StackT::empty() const { /* 實(shí)現(xiàn)同上 */ } template typename T void StackT::push(const T item) { /* 實(shí)現(xiàn)同上 */ } // ... 其他成員函數(shù)定義 // 關(guān)鍵部分顯式實(shí)例化 template class Stackint; // 告訴編譯器請(qǐng)生成int版本的Stack template class Stackdouble; // 告訴編譯器請(qǐng)生成double版本的Stack template class Stackstd::string; // 告訴編譯器請(qǐng)生成std::string版本的Stack // 你可以在這里列出所有你預(yù)計(jì)會(huì)使用的類型使用在main.cpp中#include stack.h。編譯時(shí)必須將stack.cpp一起編譯例如g main.cpp stack.cpp -o prog。工作原理編譯stack.cpp時(shí)編譯器看到了模板的全部定義以及template class Stackint;這條指令于是它為Stackint生成了所有成員函數(shù)的二進(jìn)制代碼并存入stack.o。鏈接時(shí)main.o中對(duì)Stackint方法的調(diào)用就能在stack.o中找到定義了。優(yōu)點(diǎn)真正的分離完美實(shí)現(xiàn)了接口(.h)與實(shí)現(xiàn)(.cpp)的分離。編譯加速模板代碼只在stack.cpp中被編譯一次。其他文件包含輕量級(jí)的stack.h編譯極快。隱藏實(shí)現(xiàn)用戶只需要看到簡(jiǎn)潔的聲明頭文件。缺點(diǎn)靈活性受限這是最大的代價(jià)。你必須在stack.cpp中預(yù)先知道并列出所有可能用到的類型。如果用戶想在項(xiàng)目中使用一個(gè)你沒(méi)列出的類型比如StackMyCustomClass鏈接器會(huì)報(bào)“undefined reference”錯(cuò)誤因?yàn)榫幾g器從未為這個(gè)類型生成代碼。維護(hù)負(fù)擔(dān)每當(dāng)需要支持一個(gè)新類型你都必須去修改stack.cpp文件添加一行新的顯式實(shí)例化語(yǔ)句并重新編譯該文件。適用場(chǎng)景模板需要支持的類型集合是已知的、有限的并且穩(wěn)定不變。例如一個(gè)數(shù)學(xué)庫(kù)中的Vector2/3/4模板通常只實(shí)例化float和double類型。許多大型商業(yè)庫(kù)如某些版本的Boost內(nèi)部會(huì)采用這種方式來(lái)預(yù)編譯常用類型以提升用戶編譯速度。3.3 方案三分離編譯模式 (The Separation Model) —— 使用export已廢棄或替代技巧C標(biāo)準(zhǔn)曾經(jīng)引入export關(guān)鍵字意圖讓模板能像普通函數(shù)一樣聲明和定義分離。但因?yàn)樗鼘?shí)現(xiàn)難度極大只有極少數(shù)編譯器如EDG支持且最終被C11標(biāo)準(zhǔn)標(biāo)記為廢棄在C17中正式移除。所以絕對(duì)不要在你的新項(xiàng)目中使用export。那么有沒(méi)有辦法模擬“分離編譯”呢有但本質(zhì)上是方案一的變體。我們可以通過(guò)額外的包含技巧來(lái)保持視覺(jué)上的分離。具體做法創(chuàng)建聲明頭文件stack.h同方案二。創(chuàng)建實(shí)現(xiàn)文件stack.ipp或stack.impl.h注意后綴名表示這是要被包含的實(shí)現(xiàn)。// stack.ipp (或 stack_impl.h) #ifndef STACK_IPP #define STACK_IPP // 注意這里不直接包含stack.h假設(shè)調(diào)用者已經(jīng)包含了 template typename T bool StackT::empty() const { /* 實(shí)現(xiàn) */ } // ... 其他實(shí)現(xiàn) #endif // STACK_IPP在聲明頭文件stack.h的末尾有條件地包含實(shí)現(xiàn)文件。// stack.h (末尾部分) // ... 類聲明 #ifdef STACK_IMPLEMENTATION #include stack.ipp #endif #endif // STACK_H使用方式有兩種方式A常用用戶在一個(gè)統(tǒng)一的“匯聚點(diǎn)”比如一個(gè)專門的implement.cpp中定義STACK_IMPLEMENTATION宏然后包含stack.h。這樣整個(gè)項(xiàng)目中模板只在此處被實(shí)例化一次。// implement.cpp #define STACK_IMPLEMENTATION #include stack.h方式B在stack.h中直接取消條件編譯最后一行就是#include stack.ipp。這本質(zhì)上又變回了方案一但文件在邏輯上是分開(kāi)的。優(yōu)點(diǎn)邏輯清晰在代碼管理上聲明和實(shí)現(xiàn)仍然是獨(dú)立的文件??煽氐木幾g次數(shù)通過(guò)方式A可以精確控制模板在哪個(gè)源文件中被實(shí)例化從而管理編譯依賴。缺點(diǎn)并未真正解決包含模式的本質(zhì)問(wèn)題如果采用方式B或者用戶不小心在多個(gè)文件中定義了STACK_IMPLEMENTATION依然會(huì)導(dǎo)致多次編譯。它更像是一種代碼組織風(fēng)格。適用場(chǎng)景中大型項(xiàng)目希望保持代碼文件分離的整潔性同時(shí)又愿意接受方案一的內(nèi)在限制或者希望通過(guò)一個(gè)中心文件來(lái)管理所有模板實(shí)例化。方案對(duì)比總結(jié)表特性包含模式 (Inclusion)顯式實(shí)例化 (Explicit Instantiation)分離包含模式 (Inclusion with .ipp)代碼分離否同文件是(.h/.cpp)是邏輯分離物理包含編譯速度慢N次編譯快1次編譯取決于用法可能慢或快使用靈活性高支持任何類型低僅預(yù)定義類型高支持任何類型實(shí)現(xiàn)隱藏否是否標(biāo)準(zhǔn)符合性完全符合完全符合完全符合本質(zhì)是包含推薦場(chǎng)景小型項(xiàng)目、通用庫(kù)、原型類型固定的庫(kù)、追求編譯速度注重代碼文件組織的中大型項(xiàng)目4. 實(shí)戰(zhàn)演練構(gòu)建一個(gè)可分離編譯的健壯棧模板理論說(shuō)再多不如動(dòng)手寫一遍。我們選擇**方案二顯式實(shí)例化**作為實(shí)戰(zhàn)案例因?yàn)樗钅荏w現(xiàn)“分離”的精髓并且能讓我們深入理解背后的機(jī)制。我們會(huì)構(gòu)建一個(gè)工業(yè)級(jí)強(qiáng)度的Stack模板。4.1 項(xiàng)目結(jié)構(gòu)與文件規(guī)劃首先規(guī)劃我們的項(xiàng)目目錄結(jié)構(gòu)良好的結(jié)構(gòu)是成功的一半。your_project/ ├── include/ # 對(duì)外公開(kāi)的頭文件接口 │ └── stack.h ├── src/ # 私有源文件實(shí)現(xiàn) │ └── stack.cpp └── apps/ # 示例或測(cè)試程序 └── main.cpp4.2 接口設(shè)計(jì)include/stack.h頭文件是類的門面設(shè)計(jì)要清晰、健壯、易于使用。// include/stack.h #ifndef MYLIB_STACK_H // 使用包含項(xiàng)目名的宏防止沖突 #define MYLIB_STACK_H #include cstddef // for std::size_t namespace mylib { // 放入自定義命名空間避免污染全局 /** * brief 一個(gè)基于模板的通用棧容器。 * * tparam T 棧中元素的類型。 * tparam Container 底層容器類型默認(rèn)為 std::vectorT。 * 必須提供 back(), push_back(), pop_back(), empty() 接口。 */ template typename T, typename Container std::vectorT class Stack { public: using value_type T; using container_type Container; using size_type typename Container::size_type; using reference typename Container::reference; using const_reference typename Container::const_reference; // 構(gòu)造函數(shù) Stack() default; explicit Stack(const Container cont) : c(cont) {} explicit Stack(Container cont) : c(std::move(cont)) {} // 容量相關(guān) bool empty() const noexcept { return c.empty(); } size_type size() const noexcept { return c.size(); } // 元素訪問(wèn) reference top() { check_empty(); return c.back(); } const_reference top() const { check_empty(); return c.back(); } // 修改器 void push(const value_type value) { c.push_back(value); } void push(value_type value) { c.push_back(std::move(value)); } templatetypename... Args void emplace(Args... args) { c.emplace_back(std::forwardArgs(args)...); } void pop() { check_empty(); c.pop_back(); } void swap(Stack other) noexcept(noexcept(std::swap(c, other.c))) { using std::swap; swap(c, other.c); } // 比較運(yùn)算符非成員函數(shù)在類外聲明為友元或單獨(dú)實(shí)現(xiàn) // 為簡(jiǎn)潔起見(jiàn)此處省略實(shí)際庫(kù)中應(yīng)考慮實(shí)現(xiàn)。 private: Container c; // 底層容器 void check_empty() const { if (empty()) { throw std::underflow_error(Stack::top/pop: empty stack); } } }; // 非成員函數(shù) swap 的重載用于支持 ADL (Argument-Dependent Lookup) template typename T, typename Container void swap(StackT, Container lhs, StackT, Container rhs) noexcept(noexcept(lhs.swap(rhs))) { lhs.swap(rhs); } } // namespace mylib #endif // MYLIB_STACK_H設(shè)計(jì)要點(diǎn)解析命名空間mylib將我們的代碼與標(biāo)準(zhǔn)庫(kù)及其他庫(kù)隔離開(kāi)。模板參數(shù)除了元素類型T還增加了Container參數(shù)默認(rèn)為std::vectorT。這遵循了標(biāo)準(zhǔn)庫(kù)適配器如std::stack的設(shè)計(jì)提供了靈活性未來(lái)可輕松切換為std::deque或std::list。類型別名using語(yǔ)句定義了標(biāo)準(zhǔn)容器風(fēng)格的類型別名提高了代碼的可讀性和通用性。構(gòu)造函數(shù)提供了默認(rèn)構(gòu)造、拷貝底層容器和移動(dòng)底層容器的構(gòu)造函數(shù)。explicit防止了意外的隱式轉(zhuǎn)換。異常安全noexcept修飾符正確標(biāo)識(shí)了不會(huì)拋出異常的函數(shù)如empty(),size()有助于編譯器優(yōu)化。元素訪問(wèn)安全top()和pop()在操作前會(huì)調(diào)用私有方法check_empty()檢查棧狀態(tài)避免未定義行為拋出標(biāo)準(zhǔn)的std::underflow_error異?!,F(xiàn)代C支持提供了右值引用版本的push和emplace方法支持高效地放置新元素。swap操作提供了成員函數(shù)和非成員函數(shù)版本的swap并正確使用noexcept規(guī)范符合標(biāo)準(zhǔn)庫(kù)容器的慣例。4.3 實(shí)現(xiàn)分離src/stack.cpp這是顯式實(shí)例化的核心。我們將所有成員函數(shù)的定義放在這里并在末尾列出需要預(yù)編譯的類型。// src/stack.cpp #include ../include/stack.h // 包含接口聲明 #include stdexcept // 用于 std::underflow_error #include vector #include deque // 為了演示多容器支持 #include string namespace mylib { // ------------------------------------------------------------ // 成員函數(shù)定義 // 注意所有函數(shù)都是模板定義必須放在頭文件或此文件中 // ------------------------------------------------------------ // 構(gòu)造函數(shù)定義 (已為默認(rèn)顯式定義亦可) // template typename T, typename Container // StackT, Container::Stack() default; // 帶容器參數(shù)的構(gòu)造函數(shù) template typename T, typename Container StackT, Container::Stack(const Container cont) : c(cont) {} template typename T, typename Container StackT, Container::Stack(Container cont) : c(std::move(cont)) {} // empty 和 size 已在類內(nèi)定義為inline此處無(wú)需再定義。 // 但如果定義在類外需要如下 // template typename T, typename Container // bool StackT, Container::empty() const noexcept { return c.empty(); } // top() 成員函數(shù) template typename T, typename Container typename StackT, Container::reference StackT, Container::top() { check_empty(); return c.back(); } template typename T, typename Container typename StackT, Container::const_reference StackT, Container::top() const { check_empty(); return c.back(); } // push() 成員函數(shù) template typename T, typename Container void StackT, Container::push(const value_type value) { c.push_back(value); } template typename T, typename Container void StackT, Container::push(value_type value) { c.push_back(std::move(value)); } // emplace 成員函數(shù) template typename T, typename Container template typename... Args void StackT, Container::emplace(Args... args) { c.emplace_back(std::forwardArgs(args)...); } // pop() 成員函數(shù) template typename T, typename Container void StackT, Container::pop() { check_empty(); c.pop_back(); } // swap 成員函數(shù) template typename T, typename Container void StackT, Container::swap(Stack other) noexcept(noexcept(std::swap(c, other.c))) { using std::swap; swap(c, other.c); } // 私有輔助函數(shù) check_empty template typename T, typename Container void StackT, Container::check_empty() const { if (c.empty()) { throw std::underflow_error(Stack::top/pop: empty stack); } } // ------------------------------------------------------------ // 顯式實(shí)例化部分 // 告訴編譯器請(qǐng)為以下具體類型組合生成代碼 // ------------------------------------------------------------ // 實(shí)例化默認(rèn)容器為 std::vector 的 Stack template class Stackint; template class Stackdouble; template class Stackfloat; template class Stacklong; template class Stackchar; template class Stackstd::string; // 實(shí)例化使用 std::deque 作為底層容器的 Stack template class Stackint, std::dequeint; template class Stackstd::string, std::dequestd::string; // 甚至可以實(shí)例化一個(gè)元素類型為自定義類的Stack假設(shè)有一個(gè)簡(jiǎn)單的Point類 // 注意這要求Point類的定義在實(shí)例化時(shí)可見(jiàn)即包含Point.h // #include Point.h // template class StackPoint; } // namespace mylib關(guān)鍵點(diǎn)解析包含接口首先必須包含stack.h這樣編譯器才知道Stack類的聲明。成員函數(shù)定義所有模板成員函數(shù)的定義都寫在這里。注意函數(shù)簽名必須與頭文件中的聲明完全一致包括模板參數(shù)列表、返回類型、限定符const,noexcept。typename關(guān)鍵字在定義中當(dāng)引用依賴模板參數(shù)的嵌套類型如StackT, Container::reference時(shí)必須使用typename前綴來(lái)告訴編譯器這是一個(gè)類型而不是靜態(tài)成員。顯式實(shí)例化語(yǔ)句template class Stackint;這是魔法發(fā)生的地方。這一行代碼指示編譯器請(qǐng)使用int替換模板參數(shù)T使用默認(rèn)的std::vectorint替換Container生成Stackint, std::vectorint這個(gè)具體類的所有成員函數(shù)代碼。生成的代碼將被編譯進(jìn)當(dāng)前的stack.cpp翻譯單元并最終進(jìn)入stack.o。支持多種實(shí)例化我們可以為不同的類型和不同的底層容器進(jìn)行實(shí)例化。這展示了模板的靈活性但同時(shí)也意味著你需要預(yù)見(jiàn)到所有可能的用法。4.4 客戶端使用apps/main.cpp現(xiàn)在我們來(lái)編寫一個(gè)測(cè)試程序看看如何使用這個(gè)分離編譯的棧。// apps/main.cpp #include iostream #include string // 只包含輕量級(jí)的接口頭文件 #include ../include/stack.h int main() { std::cout 測(cè)試 mylib::Stack std::endl; // 1. 測(cè)試默認(rèn)的 int 棧 (底層容器為 std::vector) mylib::Stackint intStack; std::cout intStack 初始是否為空? std::boolalpha intStack.empty() std::endl; intStack.push(42); intStack.push(100); intStack.emplace(77); // 使用 emplace 直接構(gòu)造 std::cout 壓入元素后棧頂是: intStack.top() std::endl; std::cout 棧大小: intStack.size() std::endl; intStack.pop(); std::cout 彈出一次后棧頂是: intStack.top() std::endl; // 2. 測(cè)試 std::string 棧 mylib::Stackstd::string strStack; strStack.push(Hello); strStack.push(World); std::cout \nstrStack 棧頂: strStack.top() std::endl; // 3. 測(cè)試使用不同底層容器 (std::deque) mylib::Stackdouble, std::dequedouble dequeStack; dequeStack.push(3.14159); std::cout \ndequeStack 棧頂: dequeStack.top() std::endl; // 4. 測(cè)試異常安全 mylib::Stackint emptyStack; try { // emptyStack.top(); // 這行會(huì)拋出異常 // emptyStack.pop(); // 這行也會(huì)拋出異常 std::cout \n嘗試操作空棧... std::endl; int val emptyStack.top(); // 應(yīng)該拋出異常 std::cout 錯(cuò)誤這行不應(yīng)該被執(zhí)行。 std::endl; } catch (const std::underflow_error e) { std::cout 成功捕獲異常: e.what() std::endl; } // 5. 測(cè)試 swap mylib::Stackint stackA; stackA.push(1); mylib::Stackint stackB; stackB.push(2); std::cout \n交換前: stackA.top() stackA.top() , stackB.top() stackB.top() std::endl; mylib::swap(stackA, stackB); // 或 stackA.swap(stackB); std::cout 交換后: stackA.top() stackA.top() , stackB.top() stackB.top() std::endl; std::cout \n 所有測(cè)試通過(guò) std::endl; return 0; }4.5 編譯與鏈接這是檢驗(yàn)我們分離是否成功的關(guān)鍵步驟。我們需要分別編譯main.cpp和stack.cpp然后將它們鏈接在一起。使用 GCC/Clang 命令行# 進(jìn)入項(xiàng)目根目錄 your_project/ # 編譯主程序只需要看到 stack.h 聲明 g -stdc11 -I./include -c apps/main.cpp -o build/main.o # 編譯模板實(shí)現(xiàn)和顯式實(shí)例化 g -stdc11 -I./include -c src/stack.cpp -o build/stack.o # 鏈接兩個(gè)目標(biāo)文件 g build/main.o build/stack.o -o build/stack_demo # 運(yùn)行程序 ./build/stack_demo使用 CMake (推薦) 創(chuàng)建一個(gè)CMakeLists.txt文件在項(xiàng)目根目錄cmake_minimum_required(VERSION 3.10) project(SeparatedStackDemo) set(CMAKE_CXX_STANDARD 11) # 創(chuàng)建庫(kù)目標(biāo)編譯 stack.cpp生成靜態(tài)庫(kù) libstack.a add_library(mystack STATIC src/stack.cpp) # 告訴編譯器在 include 目錄中查找頭文件 target_include_directories(mystack PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) # 創(chuàng)建可執(zhí)行文件目標(biāo) add_executable(stack_demo apps/main.cpp) # 將可執(zhí)行文件鏈接到我們的庫(kù) target_link_libraries(stack_demo mystack)然后使用CMake構(gòu)建mkdir build cd build cmake .. make ./stack_demo如果一切順利程序?qū)⒊晒幾g并運(yùn)行輸出測(cè)試結(jié)果。這證明我們的顯式實(shí)例化分離策略成功了main.cpp只看到了簡(jiǎn)潔的接口而具體的模板代碼只在stack.cpp中被編譯了一次。5. 進(jìn)階技巧與避坑指南在實(shí)際項(xiàng)目中僅僅實(shí)現(xiàn)分離還不夠我們還會(huì)遇到一些更復(fù)雜的情況和常見(jiàn)的“坑”。5.1 處理模板友元函數(shù)如果你的模板類有非成員友元函數(shù)比如重載的運(yùn)算符它們的分離會(huì)稍微麻煩一些。友元函數(shù)本身不是類的成員但它的聲明又依賴于模板類。錯(cuò)誤示例鏈接錯(cuò)誤// stack.h templatetypename T class Stack { // ... friend std::ostream operator(std::ostream os, const StackT stk); }; // stack.cpp templatetypename T std::ostream operator(std::ostream os, const StackT stk) { // 實(shí)現(xiàn) } template std::ostream operator int(std::ostream, const Stackint); // 顯式實(shí)例化問(wèn)題在于這個(gè)友元聲明引入的是一個(gè)非模板函數(shù)它是每個(gè)StackT的特例的友元。對(duì)于Stackint友元是operator(ostream, const Stackint)。但我們?cè)?cpp中定義的是一個(gè)函數(shù)模板兩者不匹配。正確做法在頭文件中定義包含模式最簡(jiǎn)單將友元函數(shù)實(shí)現(xiàn)直接放在類聲明內(nèi)部或頭文件末尾。聲明一個(gè)函數(shù)模板并使其成為友元// stack.h templatetypename T class Stack; // 前向聲明 templatetypename T std::ostream operator(std::ostream os, const StackT stk); // 函數(shù)模板聲明 templatetypename T class Stack { // ... friend std::ostream operator T(std::ostream os, const StackT stk); // 注意這里的 T };然后在stack.cpp中實(shí)現(xiàn)這個(gè)函數(shù)模板并對(duì)其進(jìn)行顯式實(shí)例化。這種方法較為復(fù)雜。實(shí)操心得對(duì)于模板類的簡(jiǎn)單友元函數(shù)我強(qiáng)烈建議直接采用包含模式在頭文件里實(shí)現(xiàn)它。這避免了復(fù)雜的語(yǔ)法和潛在的鏈接問(wèn)題除非這個(gè)函數(shù)體非常龐大且嚴(yán)重影響編譯時(shí)間。5.2 分離模式下的類型限制這是顯式實(shí)例化方案最大的痛點(diǎn)。如果用戶想使用一個(gè)你沒(méi)有預(yù)見(jiàn)的類型比如Stackstd::complexdouble鏈接器會(huì)報(bào)錯(cuò)。解決方案預(yù)判和提供常用類型在庫(kù)的stack.cpp中實(shí)例化所有你認(rèn)為用戶可能用到的常用類型如所有基本數(shù)據(jù)類型、std::string、常用標(biāo)準(zhǔn)容器等。提供“實(shí)例化頭文件”創(chuàng)建一個(gè)額外的頭文件比如stack_instantiations.ipp里面包含所有顯式實(shí)例化語(yǔ)句。然后讓用戶選擇如果用戶只用你預(yù)定義的類型他們正常鏈接你的庫(kù)即可。如果用戶需要新類型他們可以創(chuàng)建一個(gè)自己的.cpp文件包含你的stack.h和stack_instantiations.ipp或者直接復(fù)制里面的語(yǔ)句并添加他們自己的template class StackMyType;然后編譯這個(gè)新的.cpp文件并與他們的程序鏈接。妥協(xié)使用包含模式對(duì)于需要極致靈活性的通用庫(kù)組件最終可能還是得回歸包含模式并通過(guò)其他手段如預(yù)編譯頭文件PCH來(lái)緩解編譯時(shí)間問(wèn)題。5.3 編譯防火墻與Pimpl慣用法有時(shí)即使使用包含模式模板實(shí)現(xiàn)中依賴了大量沉重的頭文件如windows.h,boost/asio.hpp也會(huì)污染用戶的編譯環(huán)境拖慢編譯速度。這時(shí)可以使用“編譯防火墻”技巧結(jié)合PimplPointer to Implementation思想。思路將模板的實(shí)現(xiàn)細(xì)節(jié)封裝到一個(gè)非模板的基類或?qū)崿F(xiàn)類中這個(gè)實(shí)現(xiàn)類在單獨(dú)的.cpp里編譯對(duì)用戶不可見(jiàn)。模板類本身只持有這個(gè)實(shí)現(xiàn)類的指針并轉(zhuǎn)發(fā)調(diào)用。// stack.h (對(duì)用戶可見(jiàn)非常輕量) templatetypename T class Stack { public: Stack(); ~Stack(); void push(const T val); T pop(); private: class Impl; // 前向聲明不透明指針 std::unique_ptrImpl pImpl; }; // stack.cpp (用戶不直接編譯) #include “stack.h” #include vector #include heavy_header.h // 沉重的依賴在這里 templatetypename T class StackT::Impl { std::vectorT elems; // ... 所有具體實(shí)現(xiàn) }; templatetypename T StackT::Stack() : pImpl(std::make_uniqueImpl()) {} // ... 其他轉(zhuǎn)發(fā)函數(shù)的定義這種方法將模板的復(fù)雜性轉(zhuǎn)移到了.cpp文件中但代價(jià)是引入了動(dòng)態(tài)分配的開(kāi)銷和額外的間接層。它更適用于接口穩(wěn)定但實(shí)現(xiàn)復(fù)雜且依賴多的場(chǎng)景對(duì)于簡(jiǎn)單的棧來(lái)說(shuō)有點(diǎn)殺雞用牛刀。5.4 常見(jiàn)編譯與鏈接錯(cuò)誤排查undefined reference to Stackint::function()原因這是最典型的錯(cuò)誤。意味著鏈接器找不到Stackint成員函數(shù)的定義。排查如果使用顯式實(shí)例化檢查stack.cpp中是否有template class Stackint;語(yǔ)句。檢查你是否將stack.cpp加入了編譯鏈接過(guò)程。如果使用包含模式檢查stack.hpp中的函數(shù)定義語(yǔ)法是否正確是否遺漏了template typename T前綴或者函數(shù)簽名是否與聲明嚴(yán)格一致。注意即使你在stack.cpp里寫了實(shí)現(xiàn)但沒(méi)有進(jìn)行顯式實(shí)例化編譯器也不會(huì)為任何類型生成代碼鏈接時(shí)必然失敗。multiple definition of Stackint::function()原因在包含模式下如果你不小心在多個(gè).cpp文件中都包含了stack.hpp的實(shí)現(xiàn)部分并且這些函數(shù)定義沒(méi)有被隱式或顯式地聲明為inline那么在鏈接時(shí)就會(huì)產(chǎn)生重復(fù)定義錯(cuò)誤。解決在頭文件中定義模板函數(shù)時(shí)它們默認(rèn)就是inline的因?yàn)槊總€(gè)實(shí)例化都是獨(dú)立的。但如果你在類外定義確保它們都在頭文件里。如果采用.ipp包含方式確保該.ipp文件只被一個(gè)源文件包含通過(guò)宏控制或者確保所有定義都在頭文件內(nèi)。error: specialization after instantiation原因在同一個(gè)翻譯單元中你先隱式或顯式地實(shí)例化了一個(gè)模板然后又試圖對(duì)它進(jìn)行特化。編譯器會(huì)困惑。解決調(diào)整代碼順序確保所有特化都出現(xiàn)在任何可能的實(shí)例化之前。通常將特化代碼放在頭文件末尾、任何使用該模板的代碼之前。6. 總結(jié)與最佳實(shí)踐建議經(jīng)過(guò)這一番從原理到實(shí)戰(zhàn)的深入探索你應(yīng)該對(duì)C模板類的定義與實(shí)現(xiàn)分離有了透徹的理解。最后分享一些我總結(jié)的最佳實(shí)踐幫助你在實(shí)際項(xiàng)目中做出合適的選擇優(yōu)先考慮包含模式對(duì)于大多數(shù)項(xiàng)目尤其是模板代碼量不大、或者處于快速迭代階段時(shí)直接使用包含模式把所有代碼放在.hpp或.h文件中是最簡(jiǎn)單、最不容易出錯(cuò)的選擇。現(xiàn)代編譯器的增量編譯和預(yù)編譯頭文件PCH技術(shù)可以很大程度上緩解編譯時(shí)間問(wèn)題。謹(jǐn)慎使用顯式實(shí)例化僅在以下情況使用你正在編寫一個(gè)庫(kù)并且明確知道用戶只會(huì)使用有限的幾種類型如數(shù)值類型、字符串。編譯時(shí)間確實(shí)是項(xiàng)目的瓶頸并且模板實(shí)現(xiàn)非常復(fù)雜。你愿意承擔(dān)維護(hù)顯式實(shí)例化列表的額外開(kāi)銷。良好的文件命名習(xí)慣.h/.hpp用于包含模式或僅包含聲明的頭文件。.ipp/.impl/_inl.h常用于存放需要被包含的模板實(shí)現(xiàn)代碼以區(qū)別于普通頭文件。.cpp用于顯式實(shí)例化或非模板代碼。利用現(xiàn)代構(gòu)建工具預(yù)編譯頭文件PCH將穩(wěn)定的、常用的模板頭文件放入預(yù)編譯頭可以大幅提升編譯速度。模塊C20這是未來(lái)的終極解決方案。C模塊允許你真正地分離模板的接口和實(shí)現(xiàn)并且編譯一次后實(shí)現(xiàn)部分可以被高效地復(fù)用。如果你的項(xiàng)目可以使用C20或更高標(biāo)準(zhǔn)強(qiáng)烈建議開(kāi)始探索模塊。測(cè)試驅(qū)動(dòng)開(kāi)發(fā)無(wú)論采用哪種分離方式都要為你的模板類編寫全面的單元測(cè)試。測(cè)試應(yīng)覆蓋所有你顯式實(shí)例化的類型以及邊界情況如空棧操作。這能確保你的分離沒(méi)有引入錯(cuò)誤。回到我們最初的棧模板選擇哪種方案最終取決于你的具體需求是追求極致的編譯速度與代碼隱藏還是追求極致的靈活性與簡(jiǎn)單性。理解每種方案背后的原理和代價(jià)你就能在未來(lái)的C項(xiàng)目中游刃有余地做出最適合的架構(gòu)決策。記住沒(méi)有銀彈只有權(quán)衡。
返回列表
PREV
查看更多資訊
NEXT
返回資訊列表
五月天停停日日| 婷婷丁香激情综合色情| 久久婷婷伊人| 色色色色综合| 色五月激情基地| 亚洲九九婷婷| 人人操碰| 日本婷婷激情四射中文字幕在线观看| 色综合激情| 色色色色色五月丁香| 五月激情六月宗合| 青青草a在线| 变态另类色图| 人人操婷婷| 九九视频在线观看| 狠狠久久婷五月| 99国产精品白浆在线观看免费 | 大香蕉娱乐| 五月婷婷av| 99免费在线| 五月天婷婷在看| 五月丁香久人妻中文| 亚洲六月色| www.色五月| 26uuu亚洲| 99热99免费| 97av在线视频| 五月婷婷六月丁香免费| 婷婷香香五月| 亚洲99激情| www.久9| www.9操| 五月天激情国产综合婷婷婷| 五月天综合色| WWW丁香五月| 五月丁香综合影院| AV操逼网| 99噜噜噜在线播放| xx色综合| 26uuu另类亚洲欧美日本一| 久久亚洲婷婷综合色五月| 97色图片中文字幕视频在线观看 | 五月天婷婷无码| 午夜少妇在线观看视频| 久九男女天堂| 亚洲图色五月天| 江苏少妇性BBB搡BBB爽爽爽| 91久久国产自产拍夜夜91久久精品文字>91麻豆精品国产 | 激情5月婷婷狠狠干| 欧美精品999| 色娸娸综合网| 欧美激情综合| 日本操B视频| 一起草av| 一起草日本| 色综合99无码 | 日本WWW九九九| 高清 码 免费看片短视频| 先锋资源91| 婷婷五月无码| 国产伦理精品高清在线观看网站一区二区| 亚洲视99| 色色丁香激情五月| 影音先锋色婷婷| 亚洲 视频 导航 一区| 日韩成人AV在线播放| 久久99激情| 国产精品成人AV在线| 六月婷婷av| 色婷婷婷婷| 99爱在线视频| 五月婷久草| 久热这里| 五月婷婷色| 亚洲亚洲人成综合网络| 国产44页| 色五月xxx| 97干婷婷五月天| 五月激情基地| 996热| 五月天激情综合网| 激情久久月| 久久久久98| 五月婷俺去也| 婷婷五月成人| 亚洲深喉aV| 综合另类激情| 婷婷五月天视| 操日视频| 天堂美国久久| 五月综合亚洲色| 色综合久久天天综合网| 久久多色| 这里只有精品69| 天天干天天做| 九九色婷婷Av| 色五月婷婷少妇人妻| 丁香久久久| 人妻综合网| 国精产品一区一区三区免费视频| 中文字幕视频在线播放| 久久人妻久久| 久久免费高| 91黄址| 色欲婷婷夜夜| 另类图片激情五月| 99色在线视频观看| 99思思热只有在这里看| 国产熟妇的荡欲午夜视频| 超爽内射| 日本 @ va 免费| 99精品在线播放| 日操夜操天天操不卡| 婷婷丁香六月天| 五月婷婷,六月丁香| 99婷五月| 颜射 精品性爱av| 人人操人人干AV| AV中文网| 开心激情久久久久久久| 日本人妻伦在线中文字幕| 99re免费在线视频| 五月天激情小说网| 97人妻碰碰碰久久久久-最近国语高清| 亚洲激情网站无码| 婷婷99狠狠躁天天躁| 成人网在线视频| 99热首页在线30| 亚洲色在线观看| 91亚洲免费片| 天天日 天天草| 人操综合| 色婷婷小说| 欧洲色色| 9久热精品在线视频| 日韩在线视频中文字幕| 在线观看中文字幕亚洲| 97碰久久| 久久六月天| 啪色综合| 久综合网| 婷婷五月综合婷婷| 天天综合精品| 天天干肏夜夜| 涩涩激情五月婷婷| 五月天综合色| 大香蕉伊人99| 久久久人妻| 久久激情综合| 99色色网| 婷婷色网站| 亚洲成人电影在线免费观看| 伊人五月天在线| 26uuu成人网| 国产成人精品一区二三区熟女在线| 日韩综合久久| 激情五月天色色网| 久久这里只有精品视频26| 亚洲1区| 91精品久久久久久| 极品少妇XXXX精品少妇偷拍| 日日夜夜天天爽| 激情五月天天| 色婷婷色99国产综合精品| 久久9RE热视频精品98| 成人色五婷婷| 婷婷中文在线| 色青青电影色五月| 亚洲无码播放| 很很干在线视频| 狠狠干五月| 丁香婷婷色| …亚洲黄色在线播放日韩、av中文a…| 天天做天天爱天天玩| 五月婷婷香| 99ri6在线视频| 五月天涩涩| 99噜噜噜| 丁香婷婷综合激情五月色| 五月天自拍网| 香蕉久久国产av一区二区| 五月丁香啪啪啪啪| 婷婷综合五月色播| 99自拍视频在线观看| 五月婷婷激情综合| 狠狠狠狠狠干| 婷婷狠狠香蕉综合| 五月天婷婷激情小说电影| 丁香六月婷婷久久综合八月| 99re视频在线精品| 五月天婷婷综合| 五月丁香欧美| 日本三级日本三级三级人妇四虎| 久久色大香蕉| 99ri在线视频| 99乱视频| 亚洲色婷婷99一9|| AV在线免费网站| 五月婷婷影院| 色婷| 无码人妻一区| 婷色成人| 99自拍视频在线观看| 91人人人人人| 天天操婷婷| 六月丁香激情综合| 秋霞学生妹一二级| 亚洲色碰| 2022人人操人人看| 色综色网| www.婷婷六月天| 婷婷五月天综合在线| 大香蕉久操| 激情影院69| 亚洲超碰在线| 91色吧网| 五月丁香六月婷婷激情视频在线观看免费| 农村熟妇高潮精品A片| 五月丁香六月婷婷亚洲综合| 夜夜爽日日躁| 色婷婷丁香网| 色色色国产| 婷婷丁香五月天综合在线日韩| 99操无码视频观看| 久热免费视频| 五月天成人小说| 婷婷色五月偷拍| A级毛片高清免费不卡播放谢谢谢谢| 国产精品第一国产精品| 五月天另类激情在线| 色色亚洲五月天| 亚洲婷婷月丁香五月| 六月婷基地| 婷婷在线日韩综合| 伊人免费视频9| 久热久| 深爱激情综合| 伊人网碰碰| 99精品小视频| 色色色综合色| 天天爽天天| 综合五月激情网| 五月婷婷深深爱爱| 婷婷五月天,影院| 色婷婷久久| 色综合久久88| www.99久久久| 这里有精品99| 色婷婷六月综合| 欧美又粗又大一区二区在线观看| 天天摸天天舔在线视频| 99热欧美在线观看| 五月婷婷综合色啪首页| 亚洲久热无码| 五月丁香婷婷深深爱| 丁香九月婷婷色| 激情五月视频| 五月丁香啪| 六月婷婷色综合| 久久爱婷婷| 五月天激情久久| 激情五月婷婷丁香| 超碰在线人人| 亚洲瑟瑟精品在线| 欧美成人五月天| 国产裸舞表演WWWW| www99精品| 欧美综合五月丁香六月婷| 69久久99精品久久久久婷婷| 五月丁香亭亭| 蜜臀久久99精品久久久久久酒店| 中文字幕人妻一区二区| 狠狠色狠狠爱| 人妻久久久久| 五月婷婷视频28| http://www.sd-xiangsu.com/| 婷婷五月天成人综合网| 亚洲无码yw| 97丁香婷婷| 操骚货在线| 思思99热| 人人干女人| 久久九区| 精品皮股午夜AV| 亚洲色99| 婷婷伊人綜合中文字幕小说| 丁香青青五月天| 日韩操女| 婷婷六月天精品| 中文国产五月天| 激情五月婷| 九九热黄色| 欧类av怡春院| 成人 视频免费观看网站| 亚洲 25P| 色久丁香五| 99ri在线| 91色碰| 丁香五月香蕉在线| 日日杆天天| 天天干,天天日| 午夜在线成人网站免费观看| 99,色| 特级片神马电影| 操操人人| 九九色黄色| 欧美久热| 激情 婷婷| 九九久久精品| 九九热10| 激情综合五月开心狠狠| 国产日产亚洲系列最新| 色五月婷婷中文字幕| 五月色无码| 99精品视频网| 国产精品成av人在线视午夜片| 任你爽免费视频| 婷婷中文字幕版| 色丁香五月| 日本不卡高字幕在线2019| 欧美色图天堂网色| 日日夜夜狠狠干| 五月综合视频| 九月丁香亭亭| 六月婷婷啪啪| 色综合久久综合| 久久成人精品视频| 成 人片 黄 色 大 片| 激情五月,激情综合网| 五月天婷婷青青| 婷婷99狠狠躁天天躁中| 六月激情丁香一道本7777| 草美女在线观看视频在线播放 | 9|在线观看视频| 婷婷激情五月天综合| 亚洲色视频| 欧美成人猛片AAAAAAA| 久色网址| 五月天精品视频| 日韩天堂久久| 色综合色综合色综合色综合| 狠狠色婷婷7777久| 国产va在线视频| 久久99激情| 成片免费观看视频大全| aa久久| 欧美丁香婷婷五月| www99xxxx五月丁| 亚洲性爱99在线| 中文av网站| 五月天开心色情网| 久99视频| 色黑鬼导航| 在线观看中文字幕| WWW丁香五月| 五月天婷婷色色| 五月婷婷久久综合| 午夜 外网 精品 在线| 人人摸人人澡人人| 91丨九色丨白浆秘| 丁香六月婷| 天堂色婷婷| 97在线/亚洲| 中文字幕激情综合| 99九九精品| 黄网免费观看| 久久免费精彩视频| 深爱五月天 开心网| 欧美性生交XXXXX无码小说| anquye伊人| 乱精品一区字幕二区| site:wpjngj.com| 中文字幕在线观看视频www| 五月天激情小说婷婷| 日本人妻A片成人免费看片| 色色色色热热| 婷婷久综合| 人人草人人爱手机视频看看| 五月天婷婷久久视频| 婷婷激情五月天桃花网| 色色亚洲| 丁香五月亭亭六月综合激情网| 国产亚洲99久久精品| 精品福利911| 婷婷丁香五月社区亚洲| 日韩综合久久| 另类婷婷丁香| 91大神在线免费看视频全集男男一起操| 婷婷五月草| 99毛片| 第五色婷婷| 极品 少妇 内射| 男人大jjc女人免费视频| 激情五月婷婷开心网| 婷婷色九月| www.99热| 色噜噜五月天| 思思久久思思| 亚洲第一黄网| 九九色插| 色9999日韩国产| 国色天香伊人狠狠色| 日本97久久久精品| 99视频这里有精品| 狠狠干婷婷| av在线色五月丁香婷区久| 成人在线视频一区| 国产FREESEXVIDEOS性中国| 色五月婷婷色五月| 五月丁香综合激情在线观看| 日韩无码人妻一区二区| 激情五月婷婷| 婷婷色五月激情强奸四射| 亚洲五月天第一综合干| 七月激情六月婷婷综合在线播放| 中字幕视频在线永久在线观看免费| 欧美日韩AAA| 我爱大香蕉| 亚洲精品**不卡在线播he| 久久久久久激情| 伊人婷婷色激情丁香| 国产成人高清| 色欲色香综合网站| 婷婷色五月91啪啪| 俺也去色| 丁香婷婷激情| 爽极品色| 五月精品| 六月婷婷在线| 五月综合影院| 婷婷丁香五月六月激情| 99婷婷五月天| 丁香婷婷网| 欧美日韩日韩成人| 婷婷综合国产| 久久R激情| 激情综合五月| 五月丁香大香蕉| 激情五月,色播五月| 色爱99| 中文字幕成人| 九九碰九九爱97超| 国产免费AV在线| 五月天激情综合网| 一区中文字幕电影| 五月婷婷综合色啪首页 | 激情精品久久| 日韩熟女啪啪视频| 99碰碰。| 久久婷婷艹| 国外亚洲成AV人片在线观看| 亚洲中文字幕在线观看| 日本欧美成人片AAAA| www.色婷婷| 97热在线精品| 午夜激情综合| 超碰99在线观看| 任你日视频| 99爱在线视频| 91丨九色丨熟女| 丁香在线视频| 五月丁香六月成人| 日本99热| 99色视频| 一级操逼大片| 超碰无码老师| 五月婷婷六月丁香首页| 色碰碰| 99热99极品观看| 98永久精品| 99碰视频| 人妻爽爽爽久久久久久久久| 激情99。| 懂色AⅤ| 婷婷五月天电影区小说区| 婷婷五月天黄色小说| 国产精品色婷婷AV综合色色| 99碰网站| 色婷婷六月| 欧美成人猛片AAAAAAA| 99ER热精品视频| 欧美日韩中文国产一区发布| 久草免费福利视频| 婷婷五月综合色拍| 色色色视频免费无码| 激情网第四色| 曰韩五月丁香色婷婷无码| 国产欧美日韩综合精品一区二区| 婷婷综合天堂| 欧美色色色色色| 人妻射精AV| 婷婷五月天少妇| 夜夜撸日日骑| www.超碰在线| 日韩无码AV电影网站| 久久日婷婷| 天天操天天曰天天射| 久久五月激情| 麻豆123区| 99免费| 色五月大香蕉| 激情98色婷婷五| 97超级操操| 国产 码在线成人网站| 亚洲色欲欧美一区二区三区| 青青草成人网| 精品夜夜澡人妻无码AV| 激情宗合 激情宗合| 日韩操人| 日韩精品二三区| 日韩成人电影AV| 欧美日本韩国亚洲| 啪啪91| 这里只精品热在线18| 婷婷久久五月天| 五月天精品| 综合亚洲AV| 26uuu国产色| www.日日夜夜.com| 亚洲V国产V欧美V久久久久久| 色婷大香蕉| 天天草人人摸| av在线免费网站| 色欲一区二区三区精品A片| 综合网网欲色| 九九久久五月天综合伊人| 久青操| 六月色丁香婷婷| 久久在这里99| 天天干狠狠| 亚洲亚洲人成综合网络| 99在线观看| 六月婷婷在线| 色色色色丁香| 在线五月色播| 色五月激情网| 91操熟女| 色女人久久| 日本成人内射| 日逼免费视频| 亚洲精品V天堂中文字幕| 99在线视频资源| 日本五月视频| 一起草AV入口| 五月婷婷和六月| 九热视频| 婷婷丁香社区网| 99啪99| 欧美色色色色色色色色| 午夜婷婷六月天| 国产乱妇无乱码大黄AA片| 久久婷婷热| 亚洲熟妇无码乱子AV电影| 影音先锋男人站,影音先锋男人色资源网,影音先锋AV最新资源站,影音先锋AV资源 | 午夜婷婷五月天在线| 九九热在线精品| 日韩在线9| 激情五月天综合网| 日本乱论99| 丰满熟女人妻一区二区三| 色婷婷在线电影| 婷婷六月网| 激情丁香五月婷婷| 综合色五月天| 久久久天堂国产精品女人| 婷婷5月色| 久久99网| 在线,国产,色,热视频| 九九九九这里只有精品| 热99精品视频在线观看| 欧洲色| 婷婷五月天成人娱乐| 激情五月婷婷五月| 色亚洲中文| 99色在线| 色偷偷狠狠| www。五月,com| 第四色首页| 婷婷五月天最新网址| 五月天久久综合婷婷丁香| 欧美久久久中文字幕| 五月丁香婷婷成人综合网| 操逼综合激情网| 久久婷婷五月天蜜桃| 亚洲成Av人片乱码色第1集| 色停停五月,在线观看| 在线99色| 色色色无码| 五月丁香久久久日婷婷久久婷婷日| 午夜69成人做爰视频| 九九热视频免费观看| 91制片厂久久久国产电影| 婷婷六月丁| 精品女人九九九| www.五月天婷婷| 丁香色啪综合| 综合网狠狠| 五月婷亚洲精品AV天堂| 凹凸探花电影| 秋霞av不能| 超碰99热| 五月婷六月丁香| 久久婷婷激情四射五月天| 99色综合网| 丁香五月激情欧欧美| 久久综合图片| 激情文学久久| 天天干天天色天天干| 在线成人视频免费| 精国产品一区二区三区A片| 色婷亚洲| 九九热在线观看6| 欧美色片中文字幕久久久久| 4438激情网| 婷婷丁香五月久久| 影音先锋天天日| 成人在线视频网| 色欲一区二区三区精品A片| 婷婷五月天免费视频| 丁香五月天激情| 99热草草| 国产激情综合| 天天插天天干| 99久久99热这里只有精品| 婷婷五月天黄色网址| 777影视理论片大全在线观看| 久久99网| 婷婷五月天亚洲图片| 久久刺激网| 久99婷婷色综合| 久热九九| 婷婷成人视频| 天天狠狠色综合| 色情婷婷| 99riAv1国产在线观看| 日韩久久成人| 99九九精品| 色播五月综合网| 深爱激情网噜噜色| 亭亭丁香久久五月| 97精品欧美91久久久久久久| 久久婷婷夜| 97超级啪啪在线观看| 成片免费播放| 激情文学久久| 狼人狠狠操| 色99网站| 五月婷av| 久热这里只有精品视频免费观看| 综合激情综合啪啪| 婷婷激情五月天激情在线| 中文色婷婷| 99热在线观看99| 丰滿爆乳一区二区三区| 精品一二三区久久AAA片| 婷婷深爱五月天| 丁香婷婷91在线观看视频| av操一操| 色色色色五月| 五月天婷婷在线观看| 人人看人人草人人摸| 色五月婷婷少妇人妻| 天搞天天天天天| 亚洲第一黄网| 久久人人看| www.婷婷五月| 91久久久久久久久18| 91久久精品无码一区二区三区| 亚洲av无码影院| 五月丁香久久激情综合| 色天使久久综合| 婷婷五月天综合久久日美女| 婷婷五月丁香综合瑟瑟| 亚洲视频在线观看区| www.夜夜爱.com| 亚洲天堂青草| 十区av| 五月天天视频| 丁香六月激情| 九九热99免费视频| 东京热免费视频| 丁香五月色| 天堂成人A片永久免费网站| 超碰国产在线| 99热热热99精品丁香| 婷婷丁香五月天综合在线日韩| 专区无日本视频高清8| 久久久天堂国产精品女人| 亚洲视频在线网站| 欧美日韩一区二区三区四区| 色色色色色综合| www.97碰碰com| 二色av| 激情五月激情综合网一级丸片| 五月激情视频网| 岛国av电影网站| 狠狠干婷婷| 日本99久久| 五月天激情网站| 1024人妻无码中文字幕| 婷婷五月丁香超碰| 色波激情五月天| 激情综合另类| 99噜噜| 天天日夜夜爽| 超碰激情五月| 婷婷五月丁香基地在线视频官网| 亚州日本欧州韩美高青高潮一| 五月丁香婷婷潮喷中文字幕| 91碰超| 丁香六月天| 日本大逼91| 97人人干人人操| 色情五月天首页| 丁香五月天之婷婷影院| 日日夜夜婷婷| 久久婷鲁| 六月丁香婷婷拍拍| 久久九九热视频| 五月婷婷黄色| 5月丁香六月婷婷| 噜噜噜狠狠色综| 欧美成人一区二区三区在线视频| 日韩免费99| 99热无码精品| 夜夜久久综合网| 精品一二三区久久AAA片| 五月天伊人久久久久| 99精品这里只有免费视频| 国产26uuu| 日本色色色| 婷婷综合在线播放| site:ornaments52.com| 夜夜夜夜夜操| 日本理论久久| 天天操,天天插| 久操大香蕉| 色欲色香综合网站| 国产五月天激情小说| 婷婷激情视频| 91fuliwang| 婷婷色五月亚洲| 婷婷永久在线| www综合久久| 5月婷婷激情6月| 五月丁香六月在线| 久青操| 色色网五月激情| 九九热在线精品视频| 五月婷婷中文字幕| 天天爽天天干| 人妻免费网站| 丁香五月天五码婷婷| jiZZdr| 伊人玖玖精品| 丁香五月六月综合欧美| 中文字幕人妻一区二区| 五月天婷婷丁香蜜桃91| 色色色五月婷婷| 中文AV在线播放| 青青草原伊人网| 99伊人婷婷在线| WWW.HENHENL.| 人人色AV| 成人无码精品1区2区3区免费看| 超碰久热| 国产午夜一区二区三区| 五月久久婷婷| 日本三级韩三级99久久| 色99热| www.9797国产| 91疯狂操操操操| 狠狠五月天| 欧美日本97| 五月婷婷 激情五月| www.99精品日操伊人乱碰在线| 色婷丨日丨天丨综合久久| 五月丁香久久| 亚洲色情激情丁香五月| 在线观看免费视频| 五月丁香六月婷婷不卡免费无码| 国产精品人成A片一区二区| 激情五月深爱五月| 九九热最新| 激情五月天之六月婷婷| 欧洲MV日韩MV国产| 婷婷五月天婷婷| 色情激情五月婷婷| 婷婷综合在线| 六月丁香大香蕉| 九九av| 亚洲精99| 亚洲无码影片| 激情五月天偷拍综合网| 91婷婷伊人牛牛| 婷婷丁香五月综合| 韩国情人在线电视剧免费观看高清版全集 | 婷婷五月天成人| 丁香六月av| 五月婷婷香蕉| 无码色色色| 欧美日韩成人免费在线| 综合网五月| 婷婷综合成人五月天| 91聚色综合网| 天天色99| 五月丁香综合激情在线观看| 激情四射亚洲| 五月丁香婷婷爱| 操操操操操操婷婷五月天| 亚洲V国产V欧美V久久久久久| 婷婷五月天亚洲图片| 色播色丁香五月| www.精品99| www.99热最新视频8| 欧美97超碰| 激情久久丁香| 久久婷婷五月草视频在线播放| 色www99| 婷婷综合精品| 日韩一区二区三区无码| 国产永久一黄| 激情五月综合亚洲另类| 乱色色色| 久久婷婷精品| 久草婷妨| 夜夜操狠狠操| 激情五月天开心总和网| 丁香五月激情婷婷视频| 67久久| 五月婷婷综合激情网| 丁香五月激情六月综合| 婷婷五月色影视先锋| 专区无日本视频高清8| 婷婷网五月天| 亚洲综合丁香五月| 激情五月天啪啪| 色色欧美色色色| 91偷拍视频| 凹凸操Av| 5月婷婷综合| 久久婷婷人人| 婷婷色色欧美综合网| 九九热九九| 99热最新| 人人添人人| 黄网在线播放| 任你搞在线观看视频| 高清一区二区三区日本久| 欧美色色色色色色| 日韩av网址大全| 婷婷丁香77777| 五月天操逼网| 五月天大香蕉视频| 欧美性猛交99久久久久99按摩| 九九精品自拍| 五月婷婷亚洲| 九九九九九九九热| 99热综合在线| 91丨九色丨白浆秘| 色玖玖网| 久久大国产香蕉| 色99视频| 九九色热| 久久色天堂| 五月天开心婷婷激情网站| 久久草婷婷丁香网站| 日本激情ⅩXX免费视频| 美女网黄| 婷婷五月天久久综合88| 久久99激情五月天| 色五月,com| 国产真人做爰视频免费| 99热免费观看| 国产成人99久久亚洲综合精品| 亚洲熟妇无码乱子AV电影| 久久66成人网站| 丁香五月天精品| 婷婷国产日本欧美| 777久久精品| 俺来也综合网精品一区 | 中文字幕日韩成人| 夜夜爽天天爽| 久久精品99国产精品日本| 久久婷婷六月综合| 日韩欧美一级大黄网站| 婷婷月综合| 2020日日干| pacopacomama 070722_670 素人奥様初撮りドキュメント 103 大久保純子 | 亚洲超级碰| 激情丁香五月| 婷婷色六月| 色婷婷丁香社综合| 99超碰在线观看| 成人视频在线免费播放| 久久婷婷人人| 在线观看亚洲视频影院| 久久超级碰碰| 日本一道久久| 日本妈妈乱| www.99热最新视频8| 思思热久在线观看视频| 开心五激情网| 久久机热这里只有精品| 深爱婷婷丁香五月激情| 婷婷亚洲五月| 丁香婷婷性久久| 久久久性爱视频| 丁香五月婷婷超碰在线| 五月婷婷综合网| 99热精品在线| 蜜臀久久99精品久久久久久酒店| 亚洲美女裸体被操在线观看| 91久久国产综合久久| 天堂爱啪啪| 婷婷久久五月天| 色色网站在线免费观看视频| 激情激情激情网| 婷婷五月天中文字幕| 久久婷婷五月天| 欧美99| 麻豆AV一区二区三区| 五月刺激丁香月综合| 天天色综| 丁香五月情色| 六月婷婷影院| 国产成人亚洲综合A∨婷婷| 91精品无码| 第九色区av天堂| 五月天激情亚洲| 久久丁香五月| 5月色亭亭视频| 丁香五月六月婷婷怡红院| 色情五月天丁香社区| 99久久久久| 五月天另类小说久久小说网| 欧美日韩成人一区二区| 思思99热在线| 成人丁香五月| 日日噜噜夜夜狠狠久久丁香六月| 亚洲激情四射色| 亚洲成人中心| 综合网激情五月天| 深爱激情综合| 天天日天天爽| 日本免费91| 五月情综合| 婷婷激情人妻| 在线看九一V图片| 色情五月婷| 九九爱看亚洲| 91精品久久久久久久| 综合久久狠狠| 丁香五月电影| 婷婷日日夜夜| 五月丁香色色网| 日本天堂免费99| 亚洲热综合网在线观看| 激情综合网五月丁香| 9热网站| 三级三久久线久久99久目本WW| 丁香六月视频免费观看| 99色婷婷| 久久九九re热| 婷婷五月天日日日干干干| 精品成人久久久久久久_一二三四视| 婷婷五月天桃花网| 俺也去在线久久精品23欧美综合视频网站,丰满人妻一区二区三区在线视频53,丰满 | 欧美日韩99| 激情五月婷婷丁香| 熟惀91九色在线| 国产成人网址| av在线免费网站| 综合在线观看99| 婷婷激情五月天激情在线| 91九色欧美| 色婷婷在线视频观看| 99视频久久免费视频| 97久久精品| 一区中文字幕电影| www.minyis.com【JT】国内CDN落地页保证转化QQ2101460746 | 香蕉97碰碰碰欧美| 丁香五月天AV在线| 久热91精品| 伊人大香久久| 99热这里只有在线| 亚洲精品又粗又大又爽A片| 一级黄色影片| 天堂久久精品| 97操碰在线视频| 天天干狠狠| 99福利视频导航| 婷婷一本和五月丁香| 欧美精品18| 91精品久久久久| 97视频91| 综合激情在线| 丁香久久综合| 国产性爱一级| 婷婷色婷婷| 超碰在线观看成人视| 这里只有精品视频99| 久婷狼色诱惑在线| 婷婷五月天性| 五月丁香色停停啪啪啪| 婷婷激情五月天亚洲综合| 五月天婷婷成人网| 免费99情趣网视频| 五月丁香| 天天做天天干天天综合网| 被强行糟蹋的女人A片| 精品五月丁香| 色婷五月天| 好好日激情五月天| 曰韩少妇内射免费播放| www.婷婷五月天啪啪| 伊人无码高清| 亚洲五月综合色播| 伊人香大香蕉视频| jiqingliuyuetian| 五月丁了香蕉综合| 日韩操逼大片| 99色在线视频| a久久| 欧美亚洲婷婷五月| 丁香五月天社区婷婷| 中文av网| 色玖玖爱| 大香蕉手机视频| 亚洲 小说 欧美 激情 另类| 日本婷婷激情四射中文字幕在线观看| 这里只有精品视频在线看| 免费观看日韩成人av| 天天狠天天叉| 亚洲婷婷久久综合| 色丁香五月| 五月丁香六月婷婷不卡免费无码| 亚洲六月综合激情久久下卡| 五月丁香六月婷婷视频| a在线免费v| 亚洲激情五月天| Jh7Uf088VHafNm| 亚洲第二AV| 成人 在线 日韩| 熟女乱论网| 在线五月婷| 热久国产| 激情综合网,五月| 七七久久婷婷| 亚洲欧美综合7777色婷婷| 看黄的网站18禁| 亚洲狠狠色丁香婷婷综合久久| 五月天综合视频| 婷婷五月天影院| 青青草视频免费观看| 色久五月天| 日夜操B| 大香伊人久色| 中文字幕人妻在线| 五月婷婷亚洲综合网| 2021日韩无码| 九九这里只有精品| 天天爱夜夜爽| 深爱五月月天| 97色碰碰公开视频| 无码任你操| 激情综合五月色丁香婷婷 | 99久久99热这里只有精品| 色播五月综合网| 99热久久这里只有精品| 思思热视频| 色黑鬼导航| 丁香婷婷性久久| 夜精品无码A片一区二区蜜桃| 综合在线色婷婷| 亚洲第一色色色| 在线观看免费狠狠色丁香香综合| 亚洲亚洲人成综合网络| 激情图片婷婷| 久久开心五月婷婷| 久久综合婷婷| 99综合成人视频在线观看| 99热| 国产婷婷五月天| 五月婷婷婷婷婷| 欧美婷婷六月丁香综合色连续高潮抽搐| 色综合久久无码| 疯狂做受XXXX高潮A片动画| 玖玖婷婷色五月| 久色网五月| 激情影院内射| 亚洲AV日韩无码| 婷五月天| 精品二区| 99色婷婷| 日韩啪啪网| 五月天激情小说| 26uuu欧美激情另类| 蜜臀A∨在线水帘洞| 色噜噜狠狠色综合网| 综合另类激情| 人人艹艹艹| 五月天大香蕉| 五月丁香婷婷色| 葵花AV在线| 婷婷五月天天爽| 人人看人人草人人摸| 欧美激情五月综合| 97香蕉久久超级碰碰高清版| 五月婷婷先锋| 成人做爰黄A片免费看直播室男男| 亚洲热久久| 久久婷丁香五月| 性综合网| 久久久性爱视频| 五月丁香婷在线| 欧洲激情五月天| 久久aaa| 99在线精品视频观看免费下载| 99精品视频偷拍| 大香蕉 伊人夜| 亚洲第一av| 久久五月婷综合网| 一起操最新网址| 九九婷婷激情综合网| 国产乱妇无乱码大黄AA片| 色色色综合| www.五月天婷婷| 婷婷五月天堂| 人妻操逼视频| 天天干天天干天天干天天干天天干| 欧洲亚洲免费视频9 | 色婷婷99| 婷婷伊人| 五月婷婷啪啪| 99久久99久久综合| 激情深爱五月天| 欧美黑人巨大性生话| 色五月情| 天久综合91综合首页| 激情五月成年| 久热精品9999| 丁香六月婷婷社区| 婷婷久久影院| 久久婷婷网| 玖玖视频福利| 热久久思思热思思| 岛国av网| 天天操狠狠操| A久久| 亲子乱AV一区二区三区下载| 激情五月天视频| 亚洲超碰在线| 91男人操女人视频| 一片AV片免费播放| 91狠狠色丁香| 毛片网站谁有| 久久婷婷五月综合色播| 另类激情五月天| 中文成人在线| 欧美激情综合| 狠狠精品干练久久久无码中文字幕 | 久久五月天大美女| 颜射 精品性爱av| 99精色| www,久久久| 亚洲精品无码一区二区| 五月激情综合网婷婷| 色涩影院六月丁香| 国产成人+综合亚洲+天堂| 这里只有精品视频在线观看免费| 久久这里只有国产精品视频| 亚洲色五月婷婷| 成人精品在线| 九九热青草| 久久加勒比| 婷婷五月天色丁香| 99视频这里有精品免费观看| 成人综合视频在线| 婷婷五月天av| 欧美色色色色色色色色| 99热婷婷| 日韩人妻操逼视频| 欧美熟女99| 成人丁香婷婷| 丁香五月综合激情啪啪| 天干干夜夜操| 日日操天天操| 女人高潮内射99精品| 久久五月情| 激情涩涩网| 色无码| 国产精产国品一二三在观看| 狠狠综合区| 2020久久婷婷五月| 97色操| 无码视频国内精品久久久| 丁香婷婷色情| 99亚洲大片精品永久在线观看| 丁XX 成人| 久久小说网| 丝袜人妻| 日本VA视频| 亚洲另类在线观看| 激情五月综合第一页| 五月丁香在线国产| 成片免费观看视频大全| 久热99视频在线观看| 99五月丁香丁| 丰满人妻一区二区三区| 日日躁夜夜躁狠狠久久AV| 96精品久久久久久久久| 亚洲国产va| 99色综合| 五月天激情影院| 丁香婷婷成人网| 男男野外做爰全过程69| 一逼色综合| 五月六月婷| 操操操B| 人人爽人人射-美女久久久久久久久久-成人AV | 五月丁香六月色| 182tv992tv人之初午夜免费观看| 亚洲综合在线伊人婷| 激情综合五月婷婷| 婷婷五月丁香基| 久婷婷五月激情| 9色在线| 久久激情天堂| 久久婷婷综合五月天| 婷婷丁香熟女| 免費亭亭成人| 激情五月婷婷网| 99精品久久久久久久| 国产午夜一区二区三区| 久久综合婷婷激情| 欧美亚洲999| 久热这里| 看婷婷五月天网| 9久久精品| 婷婷九月激情| 日本一级一片免费视频| 亚洲激情五月| 五月婷婷综合在线视频小说| 亚洲AV无码成人电影| 五月花婷婷最新| 国产婷婷色综合AV蜜臀AV| 中海油常州环保涂料有限公司| 大香蕉五月丁香| 99精品视频偷拍| 久99久在线观看| 激情爱爱网站超大免费| 这里只有精品久久| www,超碰| 无码色| 色婷久九| 婷婷伊人综合中文字幕| 亚洲成人高清在线| 97色片| 色性综合| 99熟女| 色视五月天婷婷| 日木WWW视频| 五月激情网站| 99九九热视频免费| 五月丁香欧美综合|