C++?Protobuf的學(xué)習(xí)使用指南
簡(jiǎn)介
protocol buffers 是一種語(yǔ)言無(wú)關(guān)、平臺(tái)無(wú)關(guān)、可擴(kuò)展的序列化結(jié)構(gòu)數(shù)據(jù)的方法,它可用于(數(shù)據(jù))通信協(xié)議、數(shù)據(jù)存儲(chǔ)等。通信時(shí)所傳遞的信息是通過(guò)Protobuf定義的message數(shù)據(jù)結(jié)構(gòu)進(jìn)行打包,然后編譯成二進(jìn)制的碼流再進(jìn)行傳輸或者存儲(chǔ)。
Protocol buffers 是一種靈活,高效,自動(dòng)化機(jī)制的結(jié)構(gòu)數(shù)據(jù)序列化方法-可類(lèi)比 XML,但是比 XML 更?。? ~ 10倍)、更快(20 ~ 100倍)、更為簡(jiǎn)單。
Protobuf 使用的時(shí)候必須寫(xiě)一個(gè) IDL(Interface description language)文件,在里面定義好數(shù)據(jù)結(jié)構(gòu),只有預(yù)先定義了的數(shù)據(jù)結(jié)構(gòu),才能被序列化和反序列化。其中,序列化是將對(duì)象轉(zhuǎn)換二進(jìn)制數(shù)據(jù),反序列化是將二進(jìn)制數(shù)據(jù)轉(zhuǎn)換成對(duì)象。
教程
本教程提供了一個(gè)基本的C++程序員介紹使用協(xié)議緩沖區(qū)。通過(guò)創(chuàng)建一個(gè)簡(jiǎn)單的示例應(yīng)用程序,它向您展示了如何
- 在.proto文件中定義消息格式。
- 使用協(xié)議緩沖區(qū)編譯器。
- 使用C++協(xié)議緩沖區(qū)API寫(xiě)入和讀取消息。
這不是一個(gè)在C++中使用協(xié)議緩沖區(qū)的全面指南。有關(guān)更多詳細(xì)的參考信息,請(qǐng)參見(jiàn)Protocol Buffer Language Guide(proto2)、Protocol Buffer Language Guide(proto3)、C++ API Reference、C++ Generated Code Guide和Encoding Reference。
我們將要使用的示例是一個(gè)非常簡(jiǎn)單的“地址簿”應(yīng)用程序,它 可以在文件中讀取和寫(xiě)入人們的聯(lián)系方式。每個(gè)人在 地址簿具有姓名、ID、電子郵件地址和聯(lián)系電話(huà) 號(hào)碼。
如何序列化和檢索像這樣的結(jié)構(gòu)化數(shù)據(jù)?有幾個(gè) 解決這個(gè)問(wèn)題的方法:
- 原始存儲(chǔ)器中數(shù)據(jù)結(jié)構(gòu)可以以二進(jìn)制形式發(fā)送/保存。這是一種脆弱的方法,因?yàn)榻邮?閱讀代碼必須 使用完全相同的內(nèi)存布局、字節(jié)序等編譯。此外,作為 文件以原始格式積累數(shù)據(jù),而軟件的副本 這種格式的有線(xiàn)傳播,很難擴(kuò)展 格式。
- 您可以發(fā)明一種特別的方法來(lái)將數(shù)據(jù)項(xiàng)編碼為單個(gè) 字符串-例如將4個(gè)int編碼為“12:3:-23:67”。這是一個(gè)簡(jiǎn)單的和 靈活的方法,雖然它確實(shí)需要編寫(xiě)一次性編碼和 解析代碼,并且解析施加小的運(yùn)行時(shí)成本。這樣最好 用于編碼非常簡(jiǎn)單的數(shù)據(jù)。
- 將數(shù)據(jù)序列化為XML。這種方法非常有吸引力,因?yàn)閄ML是 (sort的)人類(lèi)可讀,并且有許多 語(yǔ)言。如果你想與其他人共享數(shù)據(jù),這是一個(gè)很好的選擇 應(yīng)用程序/項(xiàng)目。然而,XML是眾所周知的空間密集型,并且 編碼/解碼它可能對(duì)應(yīng)用施加巨大的性能損失。 此外,導(dǎo)航XMLDOM樹(shù)比 在類(lèi)中導(dǎo)航簡(jiǎn)單字段通常是這樣的。
您可以使用Protobuf來(lái)代替這些選項(xiàng)。協(xié)議緩沖區(qū)是 靈活、高效、自動(dòng)化的解決方案,以準(zhǔn)確地解決這一問(wèn)題。與 Protobuf,可以編寫(xiě)數(shù)據(jù)結(jié)構(gòu)的.proto
描述 希望儲(chǔ)存。Protobuf編譯器由此創(chuàng)建一個(gè)類(lèi),該類(lèi) 實(shí)現(xiàn)Protobuf數(shù)據(jù)的自動(dòng)編碼和解析 高效二進(jìn)制格式生成的類(lèi)為 組成協(xié)議緩沖區(qū)的字段,并負(fù)責(zé) 作為一個(gè)單元閱讀協(xié)議緩沖器。重要的是,協(xié)議 緩沖區(qū)格式支持以這種方式隨時(shí)間擴(kuò)展格式的想法 代碼仍然可以讀取用舊格式編碼的數(shù)據(jù)。
示例代碼
包含在源代碼包中的 “examples”目錄。
Defining Your Protocol Format
要?jiǎng)?chuàng)建地址簿應(yīng)用程序,您需要從.proto
開(kāi)始 文件。.proto
文件中的定義很簡(jiǎn)單:您為以下對(duì)象添加消息 要序列化的每個(gè)數(shù)據(jù)結(jié)構(gòu),然后為其指定名稱(chēng)和類(lèi)型 消息中的每個(gè)字段。下面是定義消息的.proto
文件, addressbook.proto
.
syntax = "proto2"; package tutorial; message Person { optional string name = 1; optional int32 id = 2; optional string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { optional string number = 1; optional PhoneType type = 2 [default = HOME]; } repeated PhoneNumber phones = 4; } message AddressBook { repeated Person people = 1; }
正如你所看到的,它的語(yǔ)法類(lèi)似于C++或Java。讓我們把每一部分都看一遍 看看它做了什么。
.proto
文件以包聲明開(kāi)始,這有助于防止不同項(xiàng)目之間的命名沖突。在C++中,生成的類(lèi)將放置在與包名稱(chēng)匹配的命名空間中。
接下來(lái),您有了消息定義。消息包含一組類(lèi)型化字段。有許多標(biāo)準(zhǔn)的簡(jiǎn)單數(shù)據(jù)類(lèi)型可用作為字段類(lèi)型,包括bool
、int32
、float
、double
和string
。你呢 還可以通過(guò)使用其他消息類(lèi)型為消息添加進(jìn)一步的結(jié)構(gòu) 字段類(lèi)型-在上面的示例中,Person
消息包含PhoneNumber
AddressBook
消息包含Person
消息。你可以的 甚至定義嵌套在其他消息中的消息類(lèi)型-正如您所看到的, PhoneNumber
類(lèi)型定義在Person
內(nèi)。您還可以定義enum
類(lèi)型 如果您希望您的一個(gè)字段具有預(yù)定義值列表中的一個(gè), 在這里,您希望指定電話(huà)號(hào)碼可以是下列電話(huà)之一 類(lèi)型:MOBILE
、HOME
或WORK
。
每個(gè)元素上的“= 1”、“= 2”標(biāo)記標(biāo)識(shí) 字段在二進(jìn)制編碼中使用。字段號(hào)1-15需要少一個(gè)字節(jié) 編碼比更高的數(shù)字,所以作為一個(gè)優(yōu)化,你可以決定使用這些 通常使用的或重復(fù)的元件的編號(hào),留下字段編號(hào)16和18。 對(duì)于不太常用的可選元素,則更高。中的每個(gè)元素 字段需要重新編碼字段編號(hào),因此重復(fù)的字段尤其 這是優(yōu)化的好方法。
每個(gè)字段必須使用以下修飾符之一進(jìn)行注釋?zhuān)?/p>
optional
:字段可以設(shè)置,也可以不設(shè)置。如果是可選字段值 則使用默認(rèn)值。對(duì)于簡(jiǎn)單類(lèi)型,可以指定 自己的默認(rèn)值,就像我們?cè)谑纠袑?duì)電話(huà)號(hào)碼type所做的那樣。 否則,將使用系統(tǒng)默認(rèn)值:數(shù)值類(lèi)型為零,為空 字符串為字符串,布爾為假。對(duì)于嵌入的消息,默認(rèn)的 值始終是消息的“默認(rèn)實(shí)例”或“原型”,它 沒(méi)有設(shè)置任何字段。調(diào)用訪(fǎng)問(wèn)器以獲取 未始終顯式設(shè)置的可選(或必需)字段 返回該字段的默認(rèn)值。repeated
:字段可以重復(fù)任何次數(shù)(包括零次)。 重復(fù)值的順序?qū)⒈A粼趨f(xié)議緩沖區(qū)中。 將重復(fù)字段視為動(dòng)態(tài)大小的數(shù)組。required
:必須提供字段的值,否則消息 將被視為“未初始化”。如果libprotobuf在debug中編譯 模式下,序列化未初始化的消息將導(dǎo)致斷言失敗。 在優(yōu)化的生成中,將跳過(guò)檢查并寫(xiě)入消息 不管怎樣。但是,解析未初始化的消息總是會(huì)失敗(通過(guò) 從parse方法返回false)。除此之外,必填字段 行為與可選字段完全相同。
Important
Required Is Forever 您應(yīng)該非常小心地將字段標(biāo)記為 required
. 如果在某個(gè)時(shí)候 如果您希望停止編寫(xiě)或發(fā)送必填字段,則 將字段更改為可選字段-老讀者將考慮消息 而該字段是不完整的,并且可能無(wú)意中拒絕或丟棄它們。 您應(yīng)該考慮為 你的緩沖器。在Google內(nèi)部 required
fields are strongly disfavored; most messages defined in proto2 syntax use 領(lǐng)域受到強(qiáng)烈反對(duì); proto 2語(yǔ)法中定義的大多數(shù)消息使用 optional
and 和 repeated
only. (Proto3 does not support 只有 (Proto3不支持 required
fields at all.) 所有的領(lǐng)域)。
您將找到編寫(xiě).proto
文件的完整指南-包括所有 可能的字段類(lèi)型-在協(xié)議緩沖區(qū)語(yǔ)言指南。 不要去尋找類(lèi)似于類(lèi)繼承的工具- protocol 緩沖器不會(huì)這樣做。
Compiling Your Protocol Buffers編譯協(xié)議緩沖區(qū)
現(xiàn)在您已經(jīng)有了.proto
,接下來(lái)您需要做的就是生成 你需要讀和寫(xiě)的類(lèi)AddressBook
(因此Person
和 PhoneNumber
)消息。為此,您需要運(yùn)行協(xié)議緩沖區(qū) 編譯器protoc
上的.proto
:
1.如果你還沒(méi)有安裝編譯器, 下載軟件包并按照在README中的說(shuō)明。
2.現(xiàn)在運(yùn)行編譯器,指定源目錄(您的 應(yīng)用程序的源代碼仍然存在-如果您 不提供值),目標(biāo)目錄(您希望 生成代碼去;通常與$SRC_DIR
相同),和路徑你的 .proto
.在這種情況下,您……:
protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto
因?yàn)樾枰狢++類(lèi),所以使用--cpp_out
選項(xiàng)-類(lèi)似 為其他支持的語(yǔ)言提供了選項(xiàng)。
這將在指定的目標(biāo)目錄中生成以下文件:
addressbook.pb.h
, the header which declares your generated classes.addressbook.pb.cc
, which contains the implementation of your classes.
The Protocol Buffer API協(xié)議緩沖區(qū)API
讓我們看一下生成的代碼,看看編譯器為您創(chuàng)建了哪些類(lèi)和函數(shù)。如果查看addressbook.pb.h,可以看到在addressbook.proto中指定的每個(gè)消息都有一個(gè)類(lèi)。仔細(xì)觀察Person類(lèi),可以看到編譯器為每個(gè)字段生成了訪(fǎng)問(wèn)器。例如,對(duì)于name、id、email和phones字段,可以使用以下方法:
// name inline bool has_name() const; inline void clear_name(); inline const ::std::string& name() const; inline void set_name(const ::std::string& value); inline void set_name(const char* value); inline ::std::string* mutable_name(); // id inline bool has_id() const; inline void clear_id(); inline int32_t id() const; inline void set_id(int32_t value); // email inline bool has_email() const; inline void clear_email(); inline const ::std::string& email() const; inline void set_email(const ::std::string& value); inline void set_email(const char* value); inline ::std::string* mutable_email(); // phones inline int phones_size() const; inline void clear_phones(); inline const ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >& phones() const; inline ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >* mutable_phones(); inline const ::tutorial::Person_PhoneNumber& phones(int index) const; inline ::tutorial::Person_PhoneNumber* mutable_phones(int index); inline ::tutorial::Person_PhoneNumber* add_phones();
正如您所看到的,getter的名稱(chēng)與小寫(xiě)的字段完全相同,而 setter方法以set_
開(kāi)始。也有has_
方法 單數(shù)(必需或可選)字段,如果該字段已 準(zhǔn)備好了。最后,每個(gè)字段都有一個(gè)clear_
方法,用于將字段恢復(fù)為 空的狀態(tài)。
雖然數(shù)字id
字段僅具有上述基本訪(fǎng)問(wèn)器集, name
和email
字段有兩個(gè)額外的方法,因?yàn)樗鼈?strings -一個(gè)mutable_
getter,讓你直接得到一個(gè)指向字符串的指針, 還有一只二傳手請(qǐng)注意,即使mutable_email()
為 尚未設(shè)置;它將自動(dòng)初始化為空字符串。如果你 如果在本例中有一個(gè)重復(fù)的消息字段,它也會(huì)有一個(gè)email
方法,而不是mutable_
方法。
重復(fù)字段也有一些特殊的方法-如果您查看 重復(fù)的phones
字段,您將看到
- 檢查重復(fù)字段的
_size
(換句話(huà)說(shuō),有多少個(gè)電話(huà)號(hào)碼Person
2#2)。 - 使用索引獲取指定的電話(huà)號(hào)碼。
- 更新指定索引處的現(xiàn)有電話(huà)號(hào)碼。
- 添加另一個(gè)電話(huà)號(hào)碼到消息,然后您可以編輯(重復(fù) 標(biāo)量類(lèi)型有一個(gè)
add_
,它只是讓你傳入新值)。
有關(guān)協(xié)議編譯器為哪些成員生成的詳細(xì)信息 任何特定字段定義,請(qǐng)參見(jiàn) C++生成代碼參考.
枚舉和嵌套類(lèi)
生成的代碼包括一個(gè)與您的PhoneType
對(duì)應(yīng)的.proto
枚舉 枚舉。您可以將此類(lèi)型稱(chēng)為Person::PhoneType
,其值為 Person::MOBILE
、Person::HOME
和Person::WORK
(實(shí)現(xiàn)細(xì)節(jié) 稍微復(fù)雜一點(diǎn),但您不需要了解它們就可以使用 enum)。
編譯器還為您生成了一個(gè)名為 Person::PhoneNumber
.如果你看一下代碼,你可以看到“真實(shí)的的” 類(lèi)實(shí)際上被稱(chēng)為Person_PhoneNumber
,但內(nèi)部定義了一個(gè)typedef Person
允許您將其視為嵌套類(lèi)。唯一的案子 這會(huì)產(chǎn)生不同的地方是,如果你想在 另一個(gè)文件-你不能在C++中正向聲明嵌套類(lèi)型,但你可以 forward-declare Person_PhoneNumber
。
標(biāo)準(zhǔn)消息方法
每個(gè)消息類(lèi)還包含許多其他方法,這些方法允許您檢查或 操縱整個(gè)消息,包括:
bool IsInitialized() const;
:檢查是否已輸入所有必填字段 準(zhǔn)備好了。string DebugString() const;
:返回一個(gè)人類(lèi)可讀的 消息,對(duì)調(diào)試特別有用。void CopyFrom(const Person& from);
:用給定的 消息的價(jià)值。void Clear();
:將所有元素清零為空狀態(tài)。
這些方法和下一節(jié)中描述的I/O方法實(shí)現(xiàn)了 Message
所有C++協(xié)議緩沖區(qū)類(lèi)共享的接口。有關(guān)更多信息, 見(jiàn) Message的完整API文檔。
解析和序列化
最后,每個(gè)協(xié)議緩沖區(qū)類(lèi)都有寫(xiě)入和閱讀消息的方法 所選類(lèi)型的二進(jìn)制格式這些措施 包括:
bool SerializeToString(string* output) const;
:序列化消息并 存儲(chǔ)給定字符串中的字節(jié)。注意,字節(jié)是二進(jìn)制的,而不是 text;我們只使用string
類(lèi)作為方便的容器。bool ParseFromString(const string& data);
:從給定的 字符串bool SerializeToOstream(ostream* output) const;
:將消息寫(xiě)入 C++ #2bool ParseFromIstream(istream* input);
:從給定的 C++istream
。
這些只是為解析和序列化提供的幾個(gè)選項(xiàng)。 再一次,請(qǐng)參閱 Message API參考 一份完整的名單。
寫(xiě)消息
現(xiàn)在讓我們嘗試使用協(xié)議緩沖區(qū)類(lèi)。你首先要做的 地址簿應(yīng)用程序能做的就是把個(gè)人詳細(xì)信息寫(xiě)到你的 地址簿文件。為此,您需要?jiǎng)?chuàng)建并填充 協(xié)議緩沖器類(lèi),然后將它們寫(xiě)入輸出流。
下面是一個(gè)程序,它從文件中讀取AddressBook
,添加一個(gè)新的 Person
基于用戶(hù)輸入將新的AddressBook
寫(xiě)回到它,并將新的undefined寫(xiě)回 文件再次。直接調(diào)用或引用由 協(xié)議編譯器突出顯示。
#include <iostream> #include <fstream> #include <string> #include "addressbook.pb.h" using namespace std; // This function fills in a Person message based on user input. void PromptForAddress(tutorial::Person* person) { cout << "Enter person ID number: "; int id; cin >> id; person->set_id(id); cin.ignore(256, '\n'); cout << "Enter name: "; getline(cin, *person->mutable_name()); cout << "Enter email address (blank for none): "; string email; getline(cin, email); if (!email.empty()) { person->set_email(email); } while (true) { cout << "Enter a phone number (or leave blank to finish): "; string number; getline(cin, number); if (number.empty()) { break; } tutorial::Person::PhoneNumber* phone_number = person->add_phones(); phone_number->set_number(number); cout << "Is this a mobile, home, or work phone? "; string type; getline(cin, type); if (type == "mobile") { phone_number->set_type(tutorial::Person::MOBILE); } else if (type == "home") { phone_number->set_type(tutorial::Person::HOME); } else if (type == "work") { phone_number->set_type(tutorial::Person::WORK); } else { cout << "Unknown phone type. Using default." << endl; } } } // Main function: Reads the entire address book from a file, // adds one person based on user input, then writes it back out to the same // file. int main(int argc, char* argv[]) { // Verify that the version of the library that we linked against is // compatible with the version of the headers we compiled against. GOOGLE_PROTOBUF_VERIFY_VERSION; if (argc != 2) { cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl; return -1; } tutorial::AddressBook address_book; { // Read the existing address book. fstream input(argv[1], ios::in | ios::binary); if (!input) { cout << argv[1] << ": File not found. Creating a new file." << endl; } else if (!address_book.ParseFromIstream(&input)) { cerr << "Failed to parse address book." << endl; return -1; } } // Add an address. PromptForAddress(address_book.add_people()); { // Write the new address book back to disk. fstream output(argv[1], ios::out | ios::trunc | ios::binary); if (!address_book.SerializeToOstream(&output)) { cerr << "Failed to write address book." << endl; return -1; } } // Optional: Delete all global objects allocated by libprotobuf. google::protobuf::ShutdownProtobufLibrary(); return 0; }
注意GOOGLE_PROTOBUF_VERIFY_VERSION
宏。這是一個(gè)很好的實(shí)踐-雖然 不是絕對(duì)必要的-在使用C++協(xié)議之前執(zhí)行此宏 緩沖庫(kù)。它驗(yàn)證您沒(méi)有意外地鏈接到 版本的庫(kù),這是不兼容的版本的標(biāo)題你 編譯。如果檢測(cè)到版本不匹配,程序?qū)⒅兄埂W⒁馐马?xiàng) 每個(gè).pb.cc
文件在啟動(dòng)時(shí)自動(dòng)調(diào)用此宏。
還請(qǐng)注意在程序結(jié)束時(shí)對(duì)ShutdownProtobufLibrary()
的調(diào)用。 所有這一切都是刪除任何全局對(duì)象分配的協(xié)議 緩沖庫(kù)。這對(duì)于大多數(shù)程序來(lái)說(shuō)是不必要的,因?yàn)檫@個(gè)過(guò)程是 無(wú)論如何都要退出,操作系統(tǒng)將負(fù)責(zé)回收其所有內(nèi)存。 但是,如果使用的內(nèi)存泄漏檢查器要求 或者如果你正在寫(xiě)一個(gè)可以被加載和卸載的庫(kù) 多次,則您可能需要強(qiáng)制Protocol Buffers 清理一切
定義消息類(lèi)型
首先讓我們看一個(gè)非常簡(jiǎn)單的例子。假設(shè)您要定義一個(gè) 請(qǐng)求消息格式,其中每個(gè)搜索請(qǐng)求都有一個(gè)查詢(xún)字符串, 您感興趣的結(jié)果的特定頁(yè)面,以及每個(gè)頁(yè)面的結(jié)果數(shù)量。下面是您用來(lái)定義消息類(lèi)型的.proto
文件。
syntax = "proto3"; message SearchRequest { string query = 1; int32 page_number = 2; int32 results_per_page = 3; }
文件的第一行指定您使用的是proto3
語(yǔ)法:如果 如果您不這樣做,協(xié)議緩沖區(qū)編譯器將假定您使用的是原型2.這一定是 文件的第一個(gè)非空、非注釋行。SearchRequest
消息定義指定了三個(gè)字段(名稱(chēng)/值 對(duì)),一個(gè)用于要包含在此類(lèi)型 留言每個(gè)字段都有一個(gè)名稱(chēng)和類(lèi)型。 指定字段類(lèi)型
在前面的示例中,所有字段都是標(biāo)量類(lèi)型:兩個(gè)整數(shù) (page_number
和results_per_page
)和字符串(query
)。也可以指定枚舉和復(fù)合類(lèi)型,如其他消息類(lèi)型的領(lǐng)域。
到此這篇關(guān)于C++ Protobuf的學(xué)習(xí)使用指南的文章就介紹到這了,更多相關(guān)C++ Protobuf內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實(shí)現(xiàn)在文本中找出某個(gè)單詞的位置信息
本文給大家分享的是使用C++實(shí)現(xiàn)在文本中找出某個(gè)單詞的位置信息,就是給出此單詞所在的行和列,有需要的小伙伴可以參考下。2016-02-02C語(yǔ)言算法的時(shí)間復(fù)雜度和空間復(fù)雜度
這篇文章主要介紹了C語(yǔ)言算法的時(shí)間復(fù)雜度和空間復(fù)雜度,算法在編寫(xiě)成可執(zhí)行程序后,運(yùn)行時(shí)需要耗費(fèi)時(shí)間資源和空間(內(nèi)存)資源,更多相關(guān)需要的朋友可以參考一下2022-07-07C++ 如何實(shí)現(xiàn)順序棧(使用模板類(lèi))
這篇文章主要介紹了C++ 如何實(shí)現(xiàn)順序棧(使用模板類(lèi)),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07C++14 新特性之函數(shù)返回值類(lèi)型推導(dǎo)
本文主要介紹了C++14 新特性之函數(shù)返回值類(lèi)型推導(dǎo),在模板編程和一些返回類(lèi)型復(fù)雜或不易直接指明的情況下非常有用,下面就來(lái)具體介紹一下,感興趣的可以了解一下2024-05-05QT中QStringListModel類(lèi)的應(yīng)用介紹
QStringListModel是最簡(jiǎn)單的模型類(lèi),具備向視圖提供字符串?dāng)?shù)據(jù)的能力,本文主要介紹了QT中QStringListModel類(lèi)的應(yīng)用介紹,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01C++數(shù)據(jù)精度問(wèn)題的解決方案(對(duì)浮點(diǎn)數(shù)保存指定位小數(shù))
對(duì)浮點(diǎn)數(shù)保存指定位小數(shù),怎么解決這個(gè)問(wèn)題呢?如果有小伙伴對(duì)C++數(shù)據(jù)精度問(wèn)題的解決方案感興趣的朋友一起看看吧2017-08-08C語(yǔ)言實(shí)現(xiàn)學(xué)生選修課程系統(tǒng)設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)學(xué)生選修課程系統(tǒng)設(shè)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-02-02C語(yǔ)言實(shí)現(xiàn)設(shè)備管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)設(shè)備管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06