Protocol Buffer 序列化

Protobuf使用

目錄

  • proto3的更新
  • 定義協議格式
  • 編譯protobuf
  • protobuf_API
    • 枚舉和嵌套類
    • 標準消息方法
    • 解析和序列化
  • 寫一條消息
  • 閱讀消息
  • 編譯
  • Protobuf擴展
  • 優化
  • 高級用法

proto3的更新

  • 在第一行非空白非注釋行,必須寫:
syntax  = "proto3";
  • 字段規則移除了 required,并把 optional 改名為 singular;
    proto2required 也是不推薦使用的。proto3 直接從語法層面上移除了 required規則。其實可以做的更徹底,把所有字段規則描述都撤銷,原來的repeated 改為在類型或字段名后加一對中括號。這樣是不是更簡潔?

  • repeated字段默認采用 packed 編碼;
    proto2 中,需要明確使用 [packed=true] 來為字段指定比較緊湊的 packed 編碼方式。

  • 移除了default 選項;
    proto2 中,可以使用 default 選項為某一字段指定默認值。在 proto3 中,字段的默認值只能根據字段類型由系統決定。也就是說,默認值全部是約定好的,而不再提供指定默認值的語法。
    在字段被設置為默認值的時候,該字段不會被序列化。這樣可以節省空間,提高效率。
    但這樣就無法區分某字段是根本沒賦值,還是賦值了默認值。這在 proto3 中問題不大,但在 proto2 中會有問題。
    比如,在更新協議的時候使用 default 選項為某個字段指定了一個與原來不同的默認值,舊代碼獲取到的該字段的值會與新代碼不一樣。

  • 枚舉類型的第一個字段必須為 0 ;

  • 移除了對分組的支持;
    分組的功能完全可以用消息嵌套的方式來實現,并且更清晰。在 proto2 中已經把分組語法標注為『過期』了。這次也算清理垃圾了。

  • 移除了對擴展的支持,新增了 Any 類型;
    Any 類型是用來替代 proto2 中的擴展的。目前還在開發中。
    proto2 中的擴展特性很像 Swift 語言中的擴展。理解起來有點困難,使用起來更是會帶來不少混亂。
    相比之下,proto3 中新增的 Any 類型有點像 C/C++ 中的 void* ,好理解,使用起來邏輯也更清晰。

  • 增加了 JSON 映射特性;
    語言的活力來自于與時俱進。當前,JSON 的流行有其充分的理由。很多『現代化』的語言都內置了對 JSON 的支持,比如 GoPHP 等。而 C++ 這種看似包羅萬象的學院派語言,因循守舊、故步自封,以致于現出了式微的苗頭。

  • map支持

map<key_type, value_type> map_field = N;
example:
map<string, Project> projects = 3;

定義協議格式

.proto文件中的定義很簡單:為要序列化的每個數據結構添加消息,然后為消息中的每個字段指定名稱和類型。這是.proto定義您的消息的文件addressbook.proto

(好的.proto文件命名風格是:packagename.messagename.proto)

syntax = "proto3";package tutorial;message Person {string name = 1;int32 id = 2;string email = 3;enum PhoneType {MOBILE = 0;HOME = 1;WORK = 2;}message PhoneNumber {string number = 1;PhoneType type = 2;}repeated PhoneNumber phones = 4;
}message AddressBook {repeated Person people = 1;
}

.proto文件以包聲明開頭,這有助于防止不同項目之間的命名沖突。在C++中,生成的類將放在與包名匹配的命名空間中。

每個元素上的“= 1”,“= 2”標記標識該字段在二進制編碼中使用的唯一“標記”。標簽號1-15需要少于一個字節來編碼而不是更高的數字,因此作為優化,您可以決定將這些標簽用于常用或重復的元素,將標簽16和更高版本留給不太常用的可選元素。重復字段中的每個元素都需要重新編碼標記號,因此重復字段特別適合此優化。

必須使用以下修飾符之一注釋每個字段:

  • required(proto3中移除):必須提供該字段的值,否則該消息將被視為“未初始化”。如果libprotobuf在調試模式下編譯,則序列化未初始化的消息將導致斷言失敗。在優化的構建中,將跳過檢查并始終寫入消息。但是,解析未初始化的消息將始終失敗(通過false從解析方法返回)。除此之外,必填字段的行為與可選字段完全相同。

  • optional(proto3中為singular):該字段可能已設置,也可能未設置。如果未設置可選字段值,則使用默認值。對于簡單類型,您可以指定自己的默認值,就像我們type在示例中為電話號碼所做的那樣。否則,使用系統默認值:數字類型為0,字符串為空字符串,boolsfalse。對于嵌入式消息,默認值始終是消息的“默認實例”或“原型”,其中沒有設置其字段。調用訪問器以獲取尚未顯式設置的可選(或必需)字段的值始終返回該字段的默認值。

  • repeated(proto3默認采用 packed 編碼):該字段可以重復任意次數(包括零)。重復值的順序將保留在協議緩沖區中。將重復字段視為動態大小的數組。

  • proto3 中移除了default選項:字段的默認值只能根據字段類型由系統決定。也就是說,默認值全部是約定好的,而不再提供指定默認值的語法。在字段被設置為默認值的時候,該字段不會被序列化。這樣可以節省空間,提高效率。

編譯protobuf

現在運行編譯器,指定源目錄(應用程序的源代碼所在的位置 - 如果不提??供值,則使用當前目錄),目標目錄(您希望生成的代碼在哪里;通常相同$SRC_DIR) ,以及你的道路.proto。:

protoc -I = $ SRC_DIR --cpp_out = $ DST_DIR $ SRC_DIR / addressbook.proto

這里都生成到當前目錄,輸入

protoc -I=. --cpp_out=. ./addressbook.proto
protoc --cpp_out=. addressbook.proto // 這種也可以

因為您需要C++類,所以使用該--cpp_out選項 - 為其他支持的語言提供了類似的選項。

這將在指定的目標目錄中生成以下文件:

  • addressbook.pb.h,標頭聲明您生成的類。
  • addressbook.pb.cc,其中包含您的類的實現。

protobuf_API

addressbook.pb.h中,可以看到指定的每條消息都有一個類addressbook.proto。對于Person類,可以看到編譯器已為每個字段生成了訪問器。 例如,對于名稱,ID,電子郵件和電話字段,有以下方法:

// 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();

對于字符串 : 一個mutable_讓你獲得指向字符串的直接指針的getter,以及一個額外的setter。請注意,mutable_email()即使email尚未設置,您也可以進行呼叫; 它將自動初始化為空字符串。如果你在這個例子中有一個單數的消息字段,它也有一個mutable_方法但不是一個set_方法。

重復的字段也有一些特殊的方法 - 如果你看一下repeated phones字段的方法,你會發現你可以

  • 檢查重復的字段的_size (換句話說,有多少電話號碼與此相關聯的 Person).

  • 使用索引獲取指定的電話號碼.

  • 更新指定索引處的現有電話號碼.

  • 在郵件中添加另一個電話號碼然后可以編輯(重復的標量類型add_只允許您傳入新值).

有關協議編譯器為任何特定字段定義生成的確切成員的詳細信息,請參閱C ++生成的代碼參考。

枚舉和嵌套類

生成的代碼包含PhoneType與您的.proto枚舉對應的枚舉。您可以參考這個類型Person::PhoneType及其作為值的Person::MOBILEPerson::HOMEPerson::WORK(實現細節是稍微復雜一點,但你并不需要了解他們使用ENUM)。

編譯器還為您生成了一個嵌套類Person::PhoneNumber。如果查看代碼,可以看到實際調用了“真實”類Person_PhoneNumber,但是在內部定義的typedef Person允許您將其視為嵌套類。唯一不同的情況是,如果你想在另一個文件中轉發聲明類 - 你不能在C++中轉發聲明嵌套類型,但你可以轉發聲明Person_PhoneNumber

標準消息方法

每個消息類還包含許多其他方法,可用于檢查或操作整個消息,包括:

  • bool IsInitialized() const; 檢查是否已設置所有必填字段。

  • string DebugString() const; 返回消息的人類可讀表示,對調試特別有用。

  • void CopyFrom(const Person& from); 使用給定消息的值覆蓋消息。

  • void Clear(); 清除所有元素回到空狀態。

以下部分中描述的這些和I / O方法實現了Message所有C ++協議緩沖區類共享的接口。有關詳細信息,請參閱完整的API文檔Message。

解析和序列化

最后,每個協議緩沖區類都有使用協議緩沖區二進制格式編寫和讀取所選類型消息的方法。這些包括:

  • bool SerializeToString(string output) const;* 序列化消息并將字節存儲在給定的字符串中。請注意,字節是二進制的,而不是文本; 我們只將該 string 類用作方便的容器。

  • bool ParseFromString(const string& data); 解析給定字符串中的消息。

  • bool SerializeToOstream(ostream output) const;* 將消息寫入給定的 C++ ostream

  • bool ParseFromIstream(istream input);* 解析來自給定 C++ 的消息 istream

這些只是解析和序列化提供的幾個選項。再次,請參閱MessageAPI參考以獲取完整列表。

寫一條消息

現在嘗試使用協議緩沖類。地址簿應用程序能夠做的第一件事是將個人詳細信息寫入地址簿文件。為此,需要創建并填充協議緩沖區類的實例,然后將它們寫入輸出流。

這是一個程序,它從文件中讀取一個AddressBook,根據用戶輸入在AddressBook文件中添加一個新的Person,然后再將新文本寫回文件。直接調用或引用協議編譯器生成的代碼的部分將突出顯示。

#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宏。在使用C ++協議緩沖區庫之前執行此宏是一種很好的做法 - 盡管不是絕對必要的。它驗證您沒有意外鏈接到與您編譯的標頭版本不兼容的庫版本。如果檢測到版本不匹配,程序將中止。請注意,每個.pb.cc文件在啟動時都會自動調用此宏。

還要注意ShutdownProtobufLibrary()程序結束時的調用。所有這一切都是刪除協議緩沖區庫分配的所有全局對象。對于大多數程序來說這是不必要的,因為該過程無論如何都要退出,操作系統將負責回收其所有內存。但是,如果您使用需要釋放每個最后一個對象的內存泄漏檢查程序,或者您正在編寫可以由單個進程多次加載和卸載的庫,那么您可能希望強制協議緩沖區清除所有內容。

閱讀消息

當然,如果無法從中獲取任何信息,那么地址簿就不會有多大用處!此示例讀取上面示例創建的文件并打印其中的所有信息。

#include <iostream>
#include <fstream>
#include <string>
#include "addressbook.pb.h"
using namespace std;// Iterates though all people in the AddressBook and prints info about them.
void ListPeople(const tutorial::AddressBook& address_book) {for (int i = 0; i < address_book.people_size(); i++) {const tutorial::Person& person = address_book.people(i);cout << "Person ID: " << person.id() << endl;cout << "  Name: " << person.name() << endl;if (person.email() !=  "") {cout << "  E-mail address: " << person.email() << endl;}for (int j = 0; j < person.phones_size(); j++) {const tutorial::Person::PhoneNumber& phone_number = person.phones(j);switch (phone_number.type()) {case tutorial::Person::MOBILE:cout << "  Mobile phone #: ";break;case tutorial::Person::HOME:cout << "  Home phone #: ";break;case tutorial::Person::WORK:cout << "  Work phone #: ";break;}cout << phone_number.number() << endl;}}
}// Main function:  Reads the entire address book from a file and prints all
//   the information inside.
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 (!address_book.ParseFromIstream(&input)) {cerr << "Failed to parse address book." << endl;return -1;}}ListPeople(address_book);// Optional:  Delete all global objects allocated by libprotobuf.google::protobuf::ShutdownProtobufLibrary();return 0;
}

編譯

g++ -Wall -std=c++11 write.cpp addressbook.pb.cc -o write `pkg-config --cflags --libs protobuf`
g++ -Wall -std=c++11 read.cpp addressbook.pb.cc -o read `pkg-config --cflags --libs protobuf`

Protobuf擴展

在釋放使用協議緩沖區的代碼之后遲早,您無疑會想要“改進”協議緩沖區的定義。如果你希望你的新緩沖區向后兼容,并且你的舊緩沖區是向前兼容的 - 而且你幾乎肯定想要這個 - 那么你需要遵循一些規則。在新版本的協議緩沖區中:

  • 不得更改任何現有字段的標記號。

  • 不得添加或刪除任何必填字段。

  • 可以刪除可選或重復的字段。

  • 可以添加新的可選或重復字段,但必須使用新的標記號(即從未在此協議緩沖區中使用的標記號,甚至不包括已刪除的字段)。

(這些規則有一些例外,但它們很少使用。)

如果您遵循這些規則,舊代碼將很樂意閱讀新消息并簡單地忽略任何新字段。對于舊代碼,已刪除的可選字段將只具有其默認值,刪除的重復字段將為空。新代碼也將透明地讀取舊消息。但是,請記住舊的消息中不會出現新的可選字段,因此您需要明確檢查它們是否已設置has_,或者在.proto文件中提供合理的默認值[default = value]標簽號后面。如果未為可選元素指定默認值,則使用特定于類型的默認值:對于字符串,默認值為空字符串。對于布爾值,默認值為false。對于數字類型,默認值為零。另請注意,如果添加了新的重復字段,則新代碼將無法判斷它是否為空(通過新代碼)或從未設置(通過舊代碼),因為沒有has_標記。

優化

C ++協議緩沖區庫經過了極大的優化。但是,正確使用可以進一步提高性能。以下是從庫中擠出最后一滴速度的一些提示:

盡可能重用消息對象。消息嘗試保留它們分配用于重用的任何內存,即使它們被清除。因此,如果您連續處理具有相同類型和類似結構的許多消息,則每次重新使用相同的消息對象來加載內存分配器是個好主意。但是,隨著時間的推移,對象會變得臃腫,特別是如果您的消息在“形狀”上有所不同,或者您偶爾構造的消息比平常大得多。

您應該通過調用SpaceUsed方法來監視消息對象的大小,并在它們變得太大時刪除它們。
您的系統內存分配器可能沒有針對從多個線程分配大量小對象進行良好優化。請嘗試使用Google的tcmalloc。

高級用法

協議緩沖區的用途不僅僅是簡單的訪問器和序列化。請務必瀏覽C ++ API參考,以了解您可以使用它們做些什么。

協議消息類提供的一個關鍵特性是反射。您可以迭代消息的字段并操縱它們的值,而無需針對任何特定的消息類型編寫代碼。使用反射的一種非常有用的方法是將協議消息轉換為與其他編碼(例如XMLJSON)之間的轉換。更高級的反射使用可能是找到兩個相同類型的消息之間的差異,或者開發一種“協議消息的正則表達式”,您可以在其中編寫與某些消息內容匹配的表達式。如果您運用自己的想象力,可以將協議緩沖區應用于比您最初預期更廣泛的問題!

Message::Reflection界面 提供反射。
我的github地址

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/448120.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/448120.shtml
英文地址,請注明出處:http://en.pswp.cn/news/448120.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何調整反光鏡和座椅的位置 為您支招

【太平洋汽車網 學車頻道】首先要進行座椅的高度調整&#xff0c;上下調整座椅讓頭部離車頂至少還有一拳的距離。如果座椅調得太高&#xff0c;車輛在顛簸時頭部容易碰到車頂&#xff0c;調得太矮了又會影響視線。然后是前后距離的調整&#xff0c;當腳踩住制動踏板至最深處時…

關于hexo與github使用過程中的問題與筆記

快速閱讀 如何用github 和hexo 創建一個blog 1.github中要新建一個與用戶名同一樣的倉庫&#xff0c; 如:homehe.github.io - 必須是io后綴。一個帳戶 只能建立一個2. 綁定域名 &#xff0c; A記錄指向ip, cname記錄指向homehe.github.io 3. 配置sshkey - 個人設置 -> SSH a…

CSS 中 的 margin、border、padding 區別 (內邊距、外邊距)

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 圖解CSS padding、margin、border屬性 W3C組織建議把所有網頁上的對像都放在一個盒(box)中&#xff0c;設計師可以通過創建定義來控制這…

CMake 常用的預定義變量

CMake 常用的預定義變量 PROJECT_NAME : 通過 project() 指定項目名稱 PROJECT_SOURCE_DIR : 工程的根目錄 PROJECT_BINARY_DIR : 執行 cmake 命令的目錄 CMAKE_CURRENT_SOURCE_DIR : 當前 CMakeList.txt 文件所在的目錄 CMAKE_CURRENT_BINARY_DIR : 編譯目錄&#xff0c;…

什么是轉向燈?使用轉向燈有何技巧?

什么是轉向燈&#xff1f;如何使用轉向燈&#xff1f;新手司機對車輛還不是很熟悉&#xff0c;如何正確使用轉向燈&#xff0c;尤其是在不同路段中該怎么正確使用轉向燈&#xff0c;成為了很多新手們的困擾之一&#xff0c;今天我們就來為大家解決這個問題吧&#xff01; 轉向燈…

基于Flask開發企業級REST API應用(一)

關于我 編程界的一名小小程序猿&#xff0c;目前在一個創業團隊任team lead&#xff0c;技術棧涉及Android、Python、Java和Go&#xff0c;這個也是我們團隊的主要技術棧。 Github&#xff1a;github.com/hylinux1024 微信公眾號&#xff1a;angrycode 前面對Python WEB框架Fla…

解決:Do not use built-in or reserved HTML elements as component id: form

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. vue 新寫了個組件&#xff0c;運行工程成功&#xff0c;但界面沒有出效果&#xff0c;F12 提示有一個警告&#xff1a; Do not use …

移動語義,右值引用

移動語義 目錄 右值引用變量是左值move庫函數移動構造函數和移動賦值移動操作庫容器和異常移動賦值操作符移動后的對象必須是可以析構的合成移動操作右值移動左值拷貝右值在無法被移動時進行拷貝拷貝和交換賦值操作與移動移動迭代器右值引用和成員函數右值與左值引用的成員函…

集合練習:登錄注冊功能

需求&#xff1a; 1、登錄賬號唯一&#xff0c;在注冊時驗證輸入的賬號是否可用&#xff0c;若已存在&#xff0c;則不可用&#xff0c;若不存在則可用2、登錄時使用賬號密碼進行驗證1 /**2 * author Administrator3 * 登錄信息 4 */5 public class UserLogin {6 …

vue 通信、傳值的多種方式(超詳細)

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 一、通過路由帶參數進行傳值 ①兩個組件 A和B,A組件通過query把orderId傳遞給B組件&#xff08;觸發事件可以是點擊事件、鉤子函數等&am…

新手開車 駕駛小秘訣要牢記

有很多人剛買到新車興奮異常&#xff0c;憑著并不熟練的駕駛技術&#xff0c;過了幾天的車癮后發現&#xff0c;剛買的車怎么出現了這樣那樣的問題 有很多人剛買到新車興奮異常&#xff0c;憑著并不熟練的駕駛技術&#xff0c;過了幾天的車癮后發現&#xff0c;剛買的車怎么出現…

chrome中Google插件導出導入

導出插件&#xff1a; 一般電腦默認將你安裝的插件存放的位置在&#xff1a;C:\Users&#xff08;用戶&#xff09;\你的電腦名稱\AppData\Local\Google\Chrome\User Data\Default\Extensions 這個文件夾下。這里的 AppData 是個隱藏文件夾&#xff0c;需要顯示隱藏文件夾才行…

科目三電子路考操作流程

如果你已經通過科目二場內五項考試&#xff0c;正準備參加科目三電子路考的話&#xff0c;不妨看看由邕江駕校李師傅操作講解的電子路考考試流程演示視頻&#xff0c;每項都有詳細介紹操作要領及評判標準哦。 科目三考試項目&#xff1a;上車準備、起步、路口左轉彎、通過學校區…

vue Bus 總線 組件間通信

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 有時候兩個組件也需要通信&#xff08;非父子關系&#xff09;。當然Vue2.0提供了Vuex&#xff0c;但在簡單的場景下&#xff0c;可以使…

C++沒有調用析構函數

github地址 在項目中遇到一個問題&#xff0c;析構函數沒有調用產生了內存泄露。 具體見valgrind檢測libevent內存泄露 我們看兩個例子 demo1 class Test1; void del(Test1* obj){delete obj; } class Test1{ public:Test1(){printf("Test1\r\n");}~Test1(){pri…

實際操作之路考的這些事

辛苦了這么久練習路考&#xff0c;今天終于實際操作到我路考了。上車以后。關上車門。把考試的單地上給考官。還沒有認真的去看考官一眼。于是就聽到考官用低沉的聲音對我說&#xff1a;你好&#xff01;當時就感覺有一點意外。沒想到考官你這么有禮貌。然后我就沒那么緊張了&a…

[C# 網絡編程系列]專題十二:實現一個簡單的FTP服務器

引言&#xff1a; 休息一個國慶節后好久沒有更新文章了&#xff0c;主要是剛開始休息完心態還沒有調整過來的&#xff0c; 現在差不多進入狀態了&#xff0c; 所以繼續和大家分享下網絡編程的知識&#xff0c;在本專題中將和大家分享如何自己實現一個簡單的FTP服務器。在我們平…

vue 2 使用 Bus.js 實現兄弟 (非父子) 組件通信 簡單案例

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 vue2中廢棄了$dispatch和$broadcast廣播和分發事件的方法。父子組件中可以用props和$emit()。如何實現非父子組件間的通信&#xff0c;可…

jenkins自動化部署

jenkins自動化部署 github地址 首先設置源碼地址&#xff0c;jenkins會從倉庫中拉取最新代碼 拉取代碼后運行shell腳本自動進行編譯 cd mediaService cmake -S . -B cmake-build-release-hisi3531 -DCMAKE_C_COMPILER/opt/hisi-linux/x86-arm/arm-hisiv500-linux/target/bin…

JS-[IIFE閉包]

JS-IIFE&閉包 IIFE(立即調用函數表達式)示例IIFE實現單例模式閉包計數器例子非閉包實現閉包實現IIFE閉包實現內存泄露解決IIFE(立即調用函數表達式) 示例 (function iife(){ //直接執行&#xff0c;無需調用console.log("hello"); })(); //上面相當于function ii…