日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++11新特性选讲 语言部分 侯捷

發(fā)布時間:2025/3/8 c/c++ 59 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++11新特性选讲 语言部分 侯捷 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

C++11新特性選講 語言部分 侯捷

本課程分為兩個部分:語言的部分和標準庫的部分。只談特性,并且是講。

本文為語言部分筆記。

  • 語言
    • Variadic Templates
    • move semantics
    • auto
    • Range-based for loop
    • Initializer list
    • Lambdas
  • 標準庫
    • type_traits
    • unodered containers
    • forward_list
    • array
    • tuple
    • concurrency
    • RegEx

關于頭文件

C++11的新特性包含語言和標準庫兩部分,后者以頭文件 header files 的形式呈現(xiàn)。

關于C++的頭文件,有以下幾點:

  • C++標準庫的頭文件均不帶 .h,如 #include <iostream>
  • 在C++中,舊式的C的頭文件(帶有 .h)依然可用,如 #include <stdio.h>
  • 建議在C++中使用,新式的C頭文件,與舊式的關系:xxx.h -> cxxx,如 #include <cstdio>

Variatic Templates

函數(shù)變參模板

假如我想設計一個函數(shù) print,它能夠接收任意數(shù)量的參數(shù),并且這個參數(shù)的類型也是任意的,就可以利用 Variatic Templates 來遞歸地實現(xiàn):

#include <iostream> void print() {} // 1template<typename T, typename... Types> // 2 void print(const T& firstArg, const Types&... args) { // 3std::cout << firstArg << std::endl;print(args...); // 4 }int main() {print("dfafda", 's', 123); }

注意這里的 ... 可不是我們口語中的省略號,而是實實在在的C++11新語法的一部分,可以將它理解為一個 pack (包),具體是什么 “包”,則取決于它出現(xiàn)在哪里。在本例中,... 共出現(xiàn)了三次:

  • 用于 template parameters,就是 template parameters pack,”模板參數(shù)包“,如2處
  • 用于 function parameters types,就是 function parameters types pack,”函數(shù)參數(shù)類型包“,如3處
  • 用于 function parameters,就是 function parameters pack,“函數(shù)參數(shù)包”,如4處

在變參模板中,如果我們想要知道可變參數(shù)的個數(shù),可通過:sizeof...(args)。

注意除了2處, 我們在1處定義了一個空參數(shù)列表的 print 函數(shù)的重載版本,它在 print 函數(shù)地參數(shù)列表中的參數(shù)被遞歸地打印完之后被調(diào)用,其實就是相當于我們 print 函數(shù)的遞歸退出的條件。

思考:以下這個 print 函數(shù)的重載版本能夠與上面的 print 函數(shù)并存嗎,如果可以,誰比較泛化,誰比較特化呢?(我們知道,兩個版本均可的情況下,較為特化的版本會被優(yōu)先調(diào)用)

template <typename... Types> void print(const Types&... args) {/* --- */ }

變參模板的花式應用

  • 萬能的hash function:多種函數(shù)重載 + 遞歸 + 函數(shù)變參模板 —>花式調(diào)用
  • tuple:類變參模板 + 繼承
  • 類變參模板

    一些”小“的新特性

    Spaces in Template Expressions

    在C++11之前,如果有模板嵌套,右側(cè)的兩個尖括號不能靠在一起,中間須有空格,否則編譯器會認為那是個右移運算符,在C++11之后編譯器變聰明了,不再需要這個空格。

    vector<list<int> >; // OK in each C++ version vector<list<int>>; // OK since C++ 11

    nullptr and std::nullptr_t

    在C++11之后,我們可以使用 nullptr (而非之前的 NULL 或者 0)來表示空指針。注意 NULL 就是一個宏,其值為0,而 nullptr 確實是個指針,其類型為 std::nullptr_t。下面的例子可以驗證:

    #include <iostream>void f(int) {std::cout << "call f(int)" << std::endl; }void f(void*) {std::cout << "call f(void*)" << std::endl; }int main() {f(0); // calls f(int)f(NULL); // calls f(int) if NULL is 0; ambiguous otherwisef(nullptr); // calls f(void*) }

    auto

    自動類型推導 auto:在C++11之后,可以用 auto 來定義變量的類型,編譯器會自動進行類型推導。

    auto i = 42; // i是int類型 double f(); auto d = f(); // d是double類型

    注意:不建議在任何時候都使用 auto ,而是推薦在這個變量的類型名稱實在是很長或者很復雜,實在是懶得打那么多字時使用,但是我們要知道變量應該是什么類型,如:

    vector<string> v; auto pos = v.begin(); // 過長auto f = [](int x) -> bool { // 過于復雜// ... }

    程序員要做到對自己的變量類型心中有數(shù)

    Uniform Initialization

    在C++11之前,許多程序員會疑惑,一個變量或者對象的初始化可能會發(fā)生于小括號,大括號,賦值運算符。如:

    vector<int> vec(3, 5); vector<int> vec {1, 2 ,3}; int a = 1;

    C++11引入一致初始化,使用大括號,在變量后面直接跟大括號,大括號中可以有任意多個元素個數(shù),設置初值,進行初始化,如:

    int values[] {1, 2, 3}; vector<int> v {2, 3, 4}; complex<double> {4.0, 3.0};

    當然之前的語法也是可用的。

    實際上,編譯器看到 {} 就會作出一個 initializer_list<T>,它關聯(lián)至一個 array<T, n>。調(diào)用函數(shù)(如ctor)時該 array 的元素被編譯器分解逐一傳給函數(shù)。需要注意的是:若某個函數(shù)參數(shù)就是個 initializer_list<T>,調(diào)用者不能傳遞數(shù)個 T 參數(shù)然后以為它們會被自動轉(zhuǎn)換為一個 intializer_list<T> 傳入,即需要自己手動將數(shù)個參數(shù)轉(zhuǎn)換為 initializer_list<T> 再進行傳值。

    比如:

    vector<string> cities {"Berlin", "New York", "London"};

    這形成一個 initializer_list<string> ,背后有個 array<string, 3>。調(diào)用 vector<string> 的 ctors(構造函數(shù))中的接收 initialize_list<string> 的版本,標準庫中所有容器都有接收這個 initializer_list<T> 的構造函數(shù)。

    但是對于我們自己的類,可能沒有接收 intializer_list<T> 這種參數(shù)的構造函數(shù),此時這個初始化列表逐一分解拆成一個一個的參數(shù)傳給函數(shù),再去找與多個單個參數(shù)相匹配的構造函數(shù)。

    initializer_list

    初始化列表是支持上面提到的大括號形式的一致性初始化的背后方法。

    為了支持用戶自定義的類的 initializer_list。C++11提供了一個類模板:std::initializer_list<T>。他可以用用于使用一包參數(shù)值來進行初始化,或者用來其他你想要處理一包參數(shù)的地方。如使用initalizer_list傳參:

    #include <iostream>void print(std::initializer_list<int> vals) {for (auto ite = vals.begin(); ite!=vals.end(); ++ite) {std::cout << *ite << "\n";} }int main() {print( {1,2,3,4} ); // 使用initalizer_list傳參 }
    • 即 {} 即可形成一個 initializer_list

    • 不同于前面的 variadic template,這里的 initializer_list 需要的是固定類型 T 的任意多個參數(shù)。也可以看做是一種容器。

    • initializer_list背后由array構建。

    • intializer_list如果被拷貝,會是淺拷貝(引用語義)

    在C++11之后的標準庫中,initializers_list 有許多應用,最常見的肯定是上面提到過的各個容器的構造函數(shù)中可以使用其作為參數(shù)。另外,在一些算法中也有應用,比如 min/max 函數(shù),在C++11之前,它們只能支持兩個元素的比較:

    std::min(1, 2);

    在C++11之后,借助 initializer_list 它可以支持多個元素的比較:

    std::min( {1, 2, 3, 4} );

    range-based for loop

    在C++11之后

    for (decl : coll) {statement; }

    如:

    std::vector<int> vec = {1, 2, 3, 4}; for (int i : vec) {std::cout << i << std::endl; }

    也可以用引用:

    std::vector<double> vec; for (auto& elem : vec) {elem *= 3; // 因為是引用,所以會改變原vector }

    類似python的for loop:

    for i in range(10):print(i)

    實際上,這種for loop的背后實現(xiàn)就是將該容器的迭代器取出來,并遍歷一遍,并將遍歷過程中的每個元素賦值到左側(cè)聲明出來的變量。

    這種for loop賦值時可能會做隱式類型轉(zhuǎn)換。

    =default, =delete

    如果你自行定義了一個 ctor,那么編譯器就不會再給你一個 default ctor;但是如果你強制加上 =default (可以空格),就可以重新獲得并使用默認的 default ctor。而如果加上 =delete,則是禁用該成員函數(shù)的使用。

    class Zoo { public:Zoo(int i1, int i2) : d1(i1), d2(i2) {} // 構造函數(shù)Zoo(const Zoo&) = delete; // 拷貝構造Zoo(Zoo&&) = default; // 移動構造Zoo& operator=(const Zoo&) = default; // 拷貝賦值Zoo& operator=(const Zoo&&) = delete; // 移動賦值virtual ~Zoo() {} // 析構函數(shù) private:int d1, d2; }

    =default

    每當我們聲明一個有參構造函數(shù)時,編譯器就不會創(chuàng)建默認構造函數(shù)。在這種情況下,我們可以使用 =default 說明符來創(chuàng)建默認的構造函數(shù)。以下代碼演示了如何創(chuàng)建:

    // use of defaulted functions #include <iostream> using namespace std;class A { public:// A user-definedA(int x){cout << "This is a parameterized constructor";}// Using the default specifier to instruct// the compiler to create the default implementation of the constructor.A() = default; };int main(){A a; //call A()A x(1); //call A(int x)cout<<endl;return 0; }

    =delete

    在C ++ 11之前,操作符delete 只有一個目的,即釋放已動態(tài)分配的內(nèi)存。而C ++ 11標準引入了此操作符的另一種用法,即:禁用成員函數(shù)的使用。這是通過附加 = delete 來完成的; 說明符到該函數(shù)聲明的結尾。

    使用 = delete 說明符禁用其使用的任何成員函數(shù)稱為explicitly deleted函數(shù)。

    雖然不限于它們,但這通常是針對隱式函數(shù)。以下示例展示了此功能派上用場的一些任務:

    禁用拷貝構造函數(shù)

    // copy-constructor using delete operator #include <iostream> using namespace std; class A { public: A(int x): m(x) { } // Delete the copy constructor A(const A&) = delete; // Delete the copy assignment operator A& operator=(const A&) = delete; int m; }; int main() { A a1(1), a2(2), a3(3); // Error, the usage of the copy assignment operator is disabled a1 = a2; // Error, the usage of the copy constructor is disabled a3 = A(a2); return 0; }

    禁用不需要的類型轉(zhuǎn)換

    // type conversion using delete operator #include <iostream> using namespace std; class A { public: A(int) {} // Declare the conversion constructor as a deleted function. Without this step, // even though A(double) isn't defined, the A(int) would accept any double value// for it's argumentand convert it to an int A(double) = delete; }; int main() { A A1(1); // Error, conversion from double to class A is disabled. A A2(100.1); return 0; }

    請注意,刪除的函數(shù)是隱式內(nèi)聯(lián)的,這一點非常重要。刪除的函數(shù)定義必須是函數(shù)的第一個聲明。換句話說,以下方法是將函數(shù)聲明為已刪除的正確方法:

    class C { public:C(C& a) = delete; };

    但是以下嘗試聲明刪除函數(shù)的方法會產(chǎn)生錯誤:

    // incorrect syntax of declaring a member function as deleted class C { public: C(); }; // Error, the deleted definition of function C must be the first declaration of the function. C::C() = delete;

    最后,明確刪除函數(shù)有什么好處?

    刪除特殊成員函數(shù)提供了一種更簡潔的方法來防止編譯器生成我們不想要的特殊成員函數(shù)。(如“禁用拷貝構造函數(shù)”示例中所示)。

    刪除正常成員函數(shù)或非成員函數(shù)可防止有問題的類型導致調(diào)用非預期函數(shù)(如“禁用不需要的參數(shù)轉(zhuǎn)換”示例中所示)。

    Big Five,指每個類的拷貝控制,即構造函數(shù)、拷貝構造函數(shù)、移動構造函數(shù)、拷貝賦值函數(shù)、移動賦值函數(shù)、析構函數(shù)。它們默認是 public 且 inline 的。

    • =default 不能用于 Big Five 之外的常規(guī)函數(shù):編譯會報錯,因為其他函數(shù)并沒有默認的版本。
    • =delete 可以用于任何函數(shù)身上(但好像沒什么意義,不需要某個函數(shù)不寫就是了,為什么要寫了再=delete呢),注意類似的 =0 只能用于虛函數(shù),這樣會使得該虛函數(shù)稱為純虛函數(shù),強迫子類重寫該函數(shù)。

    alias template (template typedef)

    帶參數(shù)的別名模板。

    template <typename T> using Vec = std::vector<T, MyAlloc<T>>;

    注意這里的 using 關鍵字并不是 C++11 的新東西,但是 using 關鍵字的這種使用方法是C++11之后的新的用來做 alias template 的方法。

    在經(jīng)過了上面的定義之后,以下兩種寫法是等價的:

    Vec<int> coll; // 等價于 std::vector<int, MyAlloc<int>> coll;

    如此我們可以方便地使用我們自己的分配器 MyAlloc 創(chuàng)建一個類型可選的 vector 對象。

    注意,大家注意到這種用法和我們的宏定義和 typedef 好像有些類似,但是實際上使用 macro 宏定義或 typedef 均無法實現(xiàn)上面的效果。

  • 若使用宏定義:

    #define Vec<T> template<typename T> std::vector<T, MyAlloc<T>>;

    我們知道宏定義就是機械地字符替換,所以在使用時:

    Vec<int> coll; // 等價于 template<typename int> std::vector<int, MyAlloc<int>>;

    完全不是我們想要的意思。

  • 若使用 typedef 也不行,因為 typedef 是不接收參數(shù)的。

    至多寫成:

    typedef std::vector<int, MyAlloc<int>> Vec;

    這當然也不是我們想要的,沒辦法指定變量的類型。

  • 注意 alias template 不能做偏特化或全特化。

    type alias (similar to typedef)

    using value_type = T; // 等價于 typedef T value_type;

    與上面的 alias template 類似,這里的 using 關鍵字的這種使用方法是C++11之后的新的用來做 type alias 的方法。

    using func = void(*)(int, int); // 等價于 typedef void (*func)(int, int);// 使用func,作為函數(shù)指針類 void example(int, int) {} func fn = example;

    后面這個例子中的 func 被定義為一種類型,它是一個函數(shù)指針類型。

    using關鍵字總結

  • using-directives,如 using namespace std;

  • using-declarations for namespace members,如 using std::cout;

  • using-declarations for class members,如 using _Base::_M_allocate;

  • type alias (since C++11),如:

    template <typename T> using Vec = std::vector<T, MyAlloc<T>>;
  • alias template (since C++11),如 using func = void(*)(int, int);

  • noexcept

    void foo() noexcept {// ... }

    程序員保證 foo() 函數(shù)不會拋出異常,讓別人/編譯器“放心地”調(diào)用該函數(shù)。

    實際上 noexcept 關鍵字還可以加參數(shù),來表示在…條件下,函數(shù)不會拋出異常,上面的 void foo() noexcept ; 就等價于 void foo() noexcept(true);, 即相當于無條件保證。

    而下面:

    void swap(Type& x, Type& y) noexcept(noexcept(x.swap(y))) {x.swap(y); }

    則意為在 x.swap(y) 不拋出異常的條件下,本函數(shù)不會拋出異常。

    補充一下,異常是這樣的,如果 A 調(diào)用 B,B 調(diào)用 C,而在 C 執(zhí)行的過程中出現(xiàn)了異常,則先看 C 有沒有寫明異常處理程序,如果有,則處理;如果沒有,則異常傳遞給 B,然后看 B 有沒有對應的異常處理程序,如果有,則處理;如果也沒有,則繼續(xù)傳遞給 A。即按照調(diào)用順序一層一層地向上傳遞,直到有對應的異常處理程序。如果用戶一直沒有異常處理程序,則執(zhí)行 std::terminate() ,進而執(zhí)行 std::abort() ,程序退出。

    override

    override 關鍵字,標明重寫,應用于虛函數(shù)身上。

    考慮下面這種情況:

    struct Base {virtual void vfunc(float) { } };struct Derived: Base {// virtual void vfunc(int) { }virtual void vfunc(int) override { } }

    子類 Derived 在繼承父類 Base 之后想要重寫父類的 void vfunc(float) 方法,但是我們知道,要重寫父類方法需要函數(shù)簽名完全一致,這里可能由于疏忽大意,將參數(shù)類型寫為了 int。這就導致子類的這個函數(shù)定義了一個新函數(shù),而非是期望中的對于父類函數(shù)的重寫了。而編譯器肯定是不知道你其實是想重寫父類方法的,因為你函數(shù)簽名的不一致,就按照一個新方法來處理了。

    在 C++11 之后,引入了 override 關鍵字,在你確實想要重寫的函數(shù)之后,加上這個關鍵字,編譯器會在你在想重寫但是函數(shù)簽名寫錯的時候提醒你,這個被標記為重寫函數(shù)的函數(shù)實際上并未進行重寫。

    final

  • 修飾類,使得該類不能被繼承

    struct Base final {};struct Derived: Base {}; // Error

    Base 類被 final 關鍵字修飾,使得其不能被繼承,下面的 Derived 試圖繼承它,會報錯。

  • 修飾虛函數(shù),使得該虛函數(shù)不能被重寫

    struct Base {virtual void func() final; }struct Derived : Base {void func(); // Error }

    Base 類本身沒有被 final 修飾,所以可以被繼承。但是其虛函數(shù) func() 被 final 關鍵字修飾,故 func() 不能被重寫。下面的 Derive 類試圖重寫它,會報錯。

  • decltype

    獲取一個變量/一個對象的類型 (即 tpyeof(a) )是非常常見的需求,但是在 C++11 之前并沒有直接提供這樣的關鍵字(僅有 typeid 等)。 decltype 可以滿足這一需求,方便地獲得變量 / 對象的類型。

    decltype 用來定義一種類型,該類型等同于一個類型表達式的結果。如 decltype(x+y) 定義了 x+y 這個表達式的返回類型。

    map<string, float> coll; decltype(coll)::value_type elem;

    在C++11之前只能:

    map<string, float>::value_type elem;

    這當然不能讓我們在未知變量 / 對象的類型的條件下知道其類型。

    decltype 的三種應用場景:

    1-用來聲明返回值類型

    有時候,函數(shù)返回值的類型取決于參數(shù)的表達式的執(zhí)行結果。然而,在C++11之前,沒有 decltype 之前,以下語句是不可能成立的:

    template<typename T1, typename T2> decltype(x+y) add(T1 x, T2 y);

    因為上面的返回值的類型使用了尚未引入且不再作用域內(nèi)的對象。

    但是在C++11之后,我們可以通過在函數(shù)的參數(shù)列表之后聲明一個返回值類型來實現(xiàn):

    template<typename T1, typename T2> auto add(T1 x, T2 y) -> decltype(x+y);

    這與 lambda 表達式聲明返回值的語法相同:
    [...](...)mutableoptthrowSpecopt?>retTypeopt{...}[...](...)\ mutable_{opt}\ throwSpec_{opt}->retType_{opt}\{...\} [...](...)?mutableopt??throwSpecopt??>retTypeopt?{...}
    2-元編程

    元編程是對模板編程的運用。

    舉例:

    typdef typename decltype(obj)::iterator iType; // 類似 typedef typename T::iterator iType; decltype(obj) anotherObj(obj);

    3-傳遞lambda的類型

    面對lambda,我們手上往往只有對象,沒有類型,要獲得其類型就得借助于 decltype 。

    如:

    auto cmp = [] (const Person& p1, const Person &p2) {return /* 給出Person類比大小的準則 */ }//... std::set<Person, decltype(cmp)> coll<cmp>;

    我們知道由于 set 是有序容器,所以在將自定義的類構成一個 set 的時候需要給出該類比大小的準則(謂詞),通常是函數(shù)、仿函數(shù)或者 lambda 表達式。但是這里我們同樣需要指定類型,這就可以用 decltype 來指定。

    lambdas

    C++11 引入了 lambdas ,允許定義一個單行的函數(shù),可以用作是參數(shù)或者局部對象。Lambdas 的引入改變了C++標準庫的使用方式(比如原來的一些仿函數(shù)謂詞,現(xiàn)在可直接用)。

    基本用法

    最簡單的 lambda 函數(shù)不接收參數(shù),并做一些簡單的事情,比如這里的打印一句話:

    [] { std::cout << "Hello Lambda" << std::endl; }

    我們可以直接調(diào)用它,就像調(diào)用普通函數(shù)和函數(shù)對象那樣,用 () :

    [] { std::cout << "Hello Lambda" << std::endl; }();

    雖然可以這樣直接低啊用,但是這樣其實沒什么意義,因為你想要打印直接打印就好了,沒必要再繞個圈子,我們通常將 lambda 函數(shù)賦值給一個變量,這樣就能像調(diào)用普通函數(shù)那樣多次調(diào)用它:

    auto l = [] { std::cout << "Hello Lambda" << std::endl; }; l(); l(); l();

    這里 lambda 對象的類型很復雜,通常也沒有必要顯式地寫出來,我們正好用前面介紹過的 C++11 中的 auto 來簡化我們的代碼。如果一定要拿到 lambda 函數(shù)對象的類型,參考上面的 decltype 的用法三。

    完整形式

    lambda 表達式的完整形式:
    [...](...)mutableoptthrowSpecopt?>retTypeopt{...}[...]\ (...)\ mutable_{opt}\ throwSpec_{opt}->retType_{opt}\{...\} [...]?(...)?mutableopt??throwSpecopt??>retTypeopt?{...}

    • lambda 函數(shù)除了少數(shù)幾處細節(jié)(如沒有默認構造函數(shù)、需要加mutable),幾乎完全等同于一個對應的函數(shù)對象
    • [] 稱為 lambda introducer ,其中存放要捕獲的外部變量表,外部變量要注意區(qū)分值傳遞和引用傳遞。如果里面放一個等號:[=, &y] 表示接收以值傳遞的形式接收所有的外界變量,不太建議用,要把自己用到的變量寫清楚。
    • () 中存放 lambda 函數(shù)的參數(shù)列表
    • {} 是 lambda 函數(shù)的函數(shù)體
    • 中間的三項(標明 opt 的)都是看情況可有可無的,但是一旦三個中有一個是出現(xiàn)的,那么小括號 () 就必須有;而若三個可選項都沒有,則 () 也是可有可無的。
    • mutable 指明參數(shù)是否可被改變,throwSpec 指明是否可能會拋出異常,retType 指明返回值的類型
    • lambda 函數(shù)默認是內(nèi)聯(lián)的

    舉例:

    #include <iostream>int main() {int id = 0;auto f = [id] () mutable {std::cout << "id: " << id << std::endl;++id;};id = 42;f();f();f();std::cout << id << std::endl; }

    輸出:

    id: 0 id: 1 id: 2 42

    注意:

  • 在定義 lambda 函數(shù) f() 時,就已經(jīng)把 id 以值傳遞的形式傳給函數(shù),因此后面 id 的改變不會影響函數(shù)真正被調(diào)用時的 id 值
  • 不加 mutable 關鍵字會報 id 是只讀變量,不能修改。
  • varidic template 變參模板詳解

    原視頻這里花了很大篇幅來講解變參模板及其應用這個極其重要的新特性,但是考慮到在新手日常編程中的使用并不是太多(而多是出現(xiàn)在大型模板庫的設計中),這里暫時略過,以后再回來補。

    Ref:

    https://blog.csdn.net/weixin_38339025/article/details/89161324

    創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎

    總結

    以上是生活随笔為你收集整理的C++11新特性选讲 语言部分 侯捷的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。