日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
C++Module詳解:模塊化編程終極指南

一、模塊接口文件

1.定義和擴(kuò)展名

模塊接口文件定義了模塊所提供功能的接口。這些文件通常具有 .cppm 擴(kuò)展名。模塊接口以聲明文件定義了某個(gè)名稱的模塊開始,這被稱為模塊聲明。模塊的名稱可以是任何有效的 C++ 標(biāo)識符。名稱可以包含點(diǎn),但不能以點(diǎn)開頭或結(jié)尾,也不能連續(xù)包含多個(gè)點(diǎn)。有效名稱的示例包括 datamodel、mycompany.datamodel、mycompany.datamodel.core、datamodel_core 等。

注意:目前,還沒有為模塊接口文件標(biāo)準(zhǔn)化的擴(kuò)展名。然而,大多數(shù)編譯器支持 .cppm(C++ 模塊)擴(kuò)展名,這也是本書所使用的。請檢查你的編譯器文檔,了解應(yīng)使用哪種擴(kuò)展名。

2.導(dǎo)出與模塊接口

模塊需要明確聲明要導(dǎo)出什么,即客戶端代碼導(dǎo)入模塊時(shí)應(yīng)該可見的內(nèi)容。從模塊導(dǎo)出實(shí)體(例如,類、函數(shù)、常量、其他模塊等)是通過 export 關(guān)鍵字完成的。模塊中未導(dǎo)出的任何內(nèi)容只在模塊內(nèi)部可見。所有導(dǎo)出實(shí)體的集合稱為模塊接口。

以下是一個(gè)名為 Person.cppm 的模塊接口文件示例,定義了一個(gè) person 模塊并導(dǎo)出了一個(gè) Person 類。注意它導(dǎo)入了  提供的功能。

export module person; // 模塊聲明
import ;      // 導(dǎo)入聲明

export class Person   // 導(dǎo)出聲明
{
public:
    Person(std::string firstName, std::string lastName)
        : m_firstName { std::move(firstName) }, m_lastName { std::move(lastName) } { }

    const std::string& getFirstName() const { return m_firstName; }
    const std::string& getLastName() const { return m_lastName; }

private:
    std::string m_firstName;
    std::string m_lastName;
};

3.使用模塊

這個(gè) Person 類可以通過導(dǎo)入 person 模塊在以下代碼中使用(test.cpp):

import person;       // 導(dǎo)入 person 模塊聲明
import ;
import ;    // 用于 std::string 的 operator<<
using namespace std;

int main() {
    Person person { "Kole", "Webb" };
    cout << person.getLastName() << ", " << person.getFirstName() << endl;
}

所有 C++ 頭文件,如 、 等,都是所謂的可導(dǎo)入頭文件,可以通過導(dǎo)入聲明導(dǎo)入。C++ 中可用的 C 頭文件不保證是可導(dǎo)入的。為了安全起見,對于 C 頭文件應(yīng)該使用 #include 而不是導(dǎo)入聲明。這樣的 #include 指令應(yīng)該放在所謂的全局模塊片段中,它必須在任何命名模塊聲明之前,并以無名模塊聲明開始。全局模塊片段只能包含預(yù)處理指令,如 #include。這樣的全局模塊片段和注釋是唯一允許出現(xiàn)在命名模塊聲明之前的內(nèi)容。

例如,如果你需要使用  C 頭文件的功能,可以按照以下方式使其可用:

module; // 開始全局模塊片段
#include  // 包含傳統(tǒng)頭文件

export module person; // 命名模塊聲明
import ;
export class Person { /* ... */

 };

4.標(biāo)準(zhǔn)術(shù)語和導(dǎo)出聲明

在標(biāo)準(zhǔn)術(shù)語中,從命名模塊聲明開始直到文件末尾的一切稱為模塊視野。幾乎任何東西都可以從模塊中導(dǎo)出,只要它有一個(gè)名稱。示例包括類定義、函數(shù)原型、類枚舉類型、使用聲明和指令、命名空間等。如果命名空間使用 export 關(guān)鍵字顯式導(dǎo)出,那么該命名空間內(nèi)的所有內(nèi)容也會(huì)自動(dòng)導(dǎo)出。例如,以下代碼片段導(dǎo)出了整個(gè) DataModel 命名空間;因此,無需顯式導(dǎo)出各個(gè)類和類型別名:

export module datamodel;
import ;

export namespace DataModel {
    class Person { /* ... */ };
    class Address { /* ... */ };
    using Persons = std::vector;
}

你還可以使用導(dǎo)出塊導(dǎo)出一整塊聲明。以下是一個(gè)示例:

export {
    namespace DataModel {
        class Person { /* ... */ };
        class Address { /* ... */ };
        using Persons = std::vector;
    }
}

二、模塊實(shí)現(xiàn)文件

1.分割接口與實(shí)現(xiàn)

一個(gè)模塊可以被分割為模塊接口文件和一個(gè)或多個(gè)模塊實(shí)現(xiàn)文件。模塊實(shí)現(xiàn)文件通常使用 .cpp 作為擴(kuò)展名。你可以自由決定將哪些實(shí)現(xiàn)移至模塊實(shí)現(xiàn)文件,以及保留哪些實(shí)現(xiàn)在模塊接口文件中。

一種選擇是將所有函數(shù)和方法的實(shí)現(xiàn)都移至模塊實(shí)現(xiàn)文件中,而只在模塊接口文件中保留函數(shù)原型、類定義等。另一種選擇是將小型函數(shù)和方法的實(shí)現(xiàn)保留在接口文件中,同時(shí)將其他函數(shù)和方法的實(shí)現(xiàn)移至實(shí)現(xiàn)文件。在這里,你有很大的靈活性。

模塊實(shí)現(xiàn)文件同樣包含一個(gè)命名模塊聲明,以指定實(shí)現(xiàn)是為哪個(gè)模塊服務(wù)的,但沒有 export 關(guān)鍵字。例如,之前的 person 模塊可以被分割為接口和實(shí)現(xiàn)文件,如下所示。這里是模塊接口文件:

export module person; // 模塊聲明
import ;

export class Person {
public:
    Person(std::string firstName, std::string lastName);
    const std::string& getFirstName() const;
    const std::string& getLastName() const;

private:
    std::string m_firstName;
    std::string m_lastName;
};

實(shí)現(xiàn)現(xiàn)在放在 Person.cpp 模塊實(shí)現(xiàn)文件中:

module person; // 模塊聲明,但沒有 export 關(guān)鍵字
using namespace std;

Person::Person(string firstName, string lastName)
    : m_firstName { move(firstName) }, m_lastName { move(lastName) } { }

const string& Person::getFirstName() const { return m_firstName; }
const string& Person::getLastName() const { return m_lastName; }

2.實(shí)現(xiàn)文件的特點(diǎn)

請注意,實(shí)現(xiàn)文件沒有為 person 模塊的導(dǎo)入聲明。module person 聲明隱含地包括了 import person 聲明。同樣值得注意的是,盡管在方法實(shí)現(xiàn)中使用了 std::string,實(shí)現(xiàn)文件也沒有對  的任何導(dǎo)入聲明。由于隱含的 import person,以及因?yàn)榇藢?shí)現(xiàn)文件是同一個(gè) person 模塊的一部分,它隱含地繼承了模塊接口文件中的  導(dǎo)入聲明。

相比之下,向 test.cpp 文件添加 import person 聲明并不會(huì)隱含地繼承  導(dǎo)入聲明,因?yàn)?nbsp;test.cpp 不是 person 模塊的一部分。關(guān)于這方面有更多內(nèi)容,在即將到來的“可見性與可達(dá)性”一節(jié)中進(jìn)行討論。

注意:模塊接口和模塊實(shí)現(xiàn)文件中的所有導(dǎo)入聲明都必須位于文件頂部,在命名模塊聲明之后,但在任何其他聲明之前。與模塊接口文件類似,如果在模塊實(shí)現(xiàn)文件中需要任何傳統(tǒng)頭文件的 #include 指令,你應(yīng)該將它們放在全局模塊片段中,其語法與模塊接口文件相同。

警告:模塊實(shí)現(xiàn)文件不能導(dǎo)出任何內(nèi)容;只有模塊接口文件可以。

三、從實(shí)現(xiàn)中分離接口

1.使用頭文件時(shí)的建議

當(dāng)使用頭文件(.h)而非模塊時(shí),強(qiáng)烈建議只在頭文件中放置聲明,并將所有實(shí)現(xiàn)移至源文件(.cpp)。這樣做的一個(gè)原因是為了提高編譯時(shí)間。如果將實(shí)現(xiàn)放在頭文件中,任何更改,即使只是修改一個(gè)注釋,也需要重新編譯包含該頭文件的所有其他源文件。對于某些頭文件,這可能會(huì)導(dǎo)致整個(gè)代碼庫的全面重新編譯。通過將實(shí)現(xiàn)放在源文件中,不觸及頭文件的情況下對這些實(shí)現(xiàn)進(jìn)行修改,意味著只需要重新編譯那個(gè)單獨(dú)的源文件。

2.模塊的不同工作方式

模塊的工作方式不同。模塊接口僅包括類定義、函數(shù)原型等,但不包括任何函數(shù)或方法的實(shí)現(xiàn),即使這些實(shí)現(xiàn)直接位于模塊接口文件中。因此,更改模塊接口文件內(nèi)的函數(shù)或方法實(shí)現(xiàn),只要不觸及接口部分(例如,函數(shù)頭 = 函數(shù)名、參數(shù)列表和返回類型),就不需要重新編譯使用該模塊的用戶。

有兩個(gè)例外:使用 inline 關(guān)鍵字標(biāo)記的函數(shù)/方法,以及模板定義。對于這兩者,編譯器需要在編譯使用它們的客戶端代碼時(shí)了解它們的完整實(shí)現(xiàn)。因此,對 inline 函數(shù)/方法或模板定義的任何更改都可能觸發(fā)客戶端代碼的重新編譯。

注意:當(dāng)頭文件中的類定義包含方法實(shí)現(xiàn)時(shí),這些方法即使沒有標(biāo)記 inline 關(guān)鍵字,也會(huì)被隱式地視為內(nèi)聯(lián)。但這對于模塊接口文件中類定義中的方法實(shí)現(xiàn)不成立。如果這些需要被內(nèi)聯(lián),它們需要被顯式地標(biāo)記為此。

盡管從技術(shù)上講,不再需要將接口與實(shí)現(xiàn)分離,但在某些情況下,我仍然建議這樣做。主要目標(biāo)應(yīng)該是擁有清晰易讀的接口。只要函數(shù)的實(shí)現(xiàn)不會(huì)遮蔽接口,使用戶難以快速理解公共接口提供了什么,就可以保留在接口中。例如,如果一個(gè)模塊有一個(gè)較大的公共接口,最好不要用實(shí)現(xiàn)來遮蔽該接口,這樣用戶可以更好地了解所提供的內(nèi)容。然而,小的 getter 和 setter 函數(shù)可以保留在接口中,因?yàn)樗鼈儗涌诘目勺x性影響不大。

從實(shí)現(xiàn)中分離接口可以通過幾種方式完成。一種選擇是將模塊分為接口和實(shí)現(xiàn)文件,如前一節(jié)所討論的。另一種選擇是在單個(gè)模塊接口文件內(nèi)分離接口和實(shí)現(xiàn)。例如,以下是在單個(gè)模塊接口文件(person.cppm)中定義的 Person 類,但將實(shí)現(xiàn)與接口分離:

export module person;
import ;

// 類定義
export class Person {
public:
    Person(std::string firstName, std::string lastName);
    const std::string& getFirstName() const;
    const std::string& getLastName() const;

private:
    std::string m_firstName;
    std::string m_lastName;
};

// 實(shí)現(xiàn)
Person::Person(std::string firstName, std::string lastName)
    : m_firstName { std::move(firstName) }, m_lastName { std::move(last

Name) } { }

const std::string& Person::getFirstName() const { return m_firstName; }
const std::string& Person::getLastName() const { return m_lastName; }

四、可見性與可達(dá)性

1.引入模塊的影響

正如之前提到的,當(dāng)你在非 person 模塊的另一個(gè)源文件中導(dǎo)入 person 模塊(例如在 test.cpp 文件中),你并沒有隱含地繼承 person 模塊接口文件中的  導(dǎo)入聲明。因此,如果沒有在 test.cpp 中顯式導(dǎo)入 ,std::string 名稱將不可見,意味著以下突出顯示的代碼行將無法編譯:

import person;

int main() {
    std::string str;
    Person person { "Kole", "Webb" };
    const std::string& lastName { person.getLastName() };
}

然而,即使沒有向 test.cpp 添加  的顯式導(dǎo)入,以下代碼行仍能正常工作:

const auto& lastName { person.getLastName() };
auto length { lastName.length() };

2.為什么這樣工作?

在 C++ 中,實(shí)體的可見性和可達(dá)性是不同的。通過導(dǎo)入 person 模塊, 中的功能變得可達(dá)但不可見??蛇_(dá)類的成員函數(shù)自動(dòng)變得可見。這意味著你可以使用  中的某些功能,例如使用 auto 類型推導(dǎo)將 getLastName() 的結(jié)果存儲在變量中,并在其上調(diào)用諸如 length() 之類的方法。

要使 std::string 名稱在 test.cpp 中可見,需要顯式導(dǎo)入 。當(dāng)你想使用例如 operator<< 這樣的功能時(shí),也需要這樣的顯式導(dǎo)入。這是因?yàn)?nbsp;operator<< 不是 std::string 的方法,而是一個(gè)非成員函數(shù),只有導(dǎo)入  后才會(huì)變得可見。

cout << person.getLastName() << endl;

分享文章:C++Module詳解:模塊化編程終極指南
網(wǎng)頁URL:http://m.5511xx.com/article/cdgcigi.html