學習日記-24/7/26

iuk11發表於2024-07-26

對QT控制元件使用差很多,不全面。
不太會看文件,自己對於新方法無法適應。

記憶體洩漏的現象,如何檢查程式碼中的記憶體洩露?
已分配的記憶體空間在使用完畢後未被釋放,導致可用記憶體減少並最終可能導致系統效能下降或程式崩潰。
方法一:程式碼審查和靜態分析工具(自己查)
方法二:動態分析工具(Valgrind)
方法三:記憶體洩漏檢測庫(gcc -fsanitize=address -g your_program.c -o your_program)(在程式碼中包含#include <mcheck.h>並在main函式開始處呼叫mtrace())
方法四:手動跟蹤:日誌記錄、智慧指標(自動管理記憶體分配和釋放)

  • 動態多型(執行時多型)/ 靜態多型(編譯時多型)
    靜態:函式過載、模板(編譯的時候就繫結)
    動態:虛擬函式實現(執行的時候繫結)

  • 虛擬函式 虛表指標 虛表
    虛擬函式:基類中宣告的函式,使用virtual關鍵字。它允許派生類重寫該函式,從而在執行時決定呼叫哪一個版本的函式。透過虛擬函式可以實現多型性。
    虛表指標:為了實現執行時的多型性,編譯器會在每個包含虛擬函式的類中插入一個隱藏的成員指標,稱為虛表指標(vptr)。這個指標指向該類的虛表(vtable)。
    虛表:是一個函式指標表,它儲存每個虛擬函式的地址。每個包含虛擬函式的類都有一個虛表。順序與它們在類中的宣告順序一致。
    工作原理:
    當一個包含虛擬函式的物件建立時,vptr被初始化為指向該物件所屬類的虛表。
    在呼叫虛擬函式時,透過vptr查詢虛表中相應的函式指標,並呼叫實際的函式實現。

  • 每個物件例項包含一個指向該虛擬函式表的指標
  • 抽象類是包含至少一個純虛擬函式的類
  • 抽象類
    定義方法/介面,提供預設行為,促進程式碼複用。
    不能例項化!!

  • 回撥函式
    回撥函式是指透過將函式指標作為引數傳遞給另一個函式,從而在特定事件或任務完成時呼叫該函式。
    在C++中,回撥函式可以透過函式指標、函式物件(仿函式)、以及lambda表示式來實現。
    lambda表示式是一種簡潔的方式來定義匿名函式。

什麼叫協議:指定一個能夠交流的標準。

協議(Protocol)在電腦科學和電信領域中,是指一組規則或標準,這些規則或標準定義了計算機之間如何進行通訊或資料交換。協議規定了資料格式、傳輸方式、錯誤檢測和糾正方法等內容,以確保在不同裝置或系統之間進行可靠和高效的通訊。

什麼叫tcp:

TCP,全稱是傳輸控制協議(Transmission Control Protocol),是Internet協議族的一部分,用於管理資料在網路中的傳輸。它是一種面向連線的協議提供可靠、順序的位元組流傳輸。TCP在網路通訊中扮演著重要的角色,確保資料能夠從一個網路裝置傳輸到另一個網路裝置,且資料不會丟失、重複或失序。

守護程序詳細內容,怎麼除錯

std::move

C++11 委託建構函式,繼承建構函式

預設建構函式
初始化建構函式(有引數)
複製建構函式
移動建構函式(move和右值引用) :接受一個右值引用引數,透過“移動”資源來初始化新物件,而不是複製。
委託建構函式 :一個建構函式呼叫另一個建構函式來簡化程式碼和減少重複。
轉換建構函式 :可以接受單個引數並實現從該引數型別向類型別轉換的建構函式。

class MyClass {
public:
    int x;
    MyClass(int val) : x(val) {
        // 轉換建構函式
        std::cout << "Conversion constructor called with value: " << x << std::endl;
    }
};

int main() {
    MyClass obj = 10;  // 呼叫轉換建構函式
    return 0;
}

可變引數模板(variadic templates)是C++11引入的一種強大特性,允許定義能夠接受可變數量引數的模板。

列表初始化 {} 👈就是用這個直接初始化

直接列表初始化:使用建構函式直接進行初始化。

複製列表初始化:用於初始化非類型別的物件,語法上更類似於傳統的複製初始化。

作用域列舉(不會發生隱式轉換)

在C++11及以後,引入了作用域列舉(scoped enumeration),也稱為強型別列舉(strongly-typed enumeration),用於避免傳統列舉(unscoped enumeration)的一些問題,特別是隱式轉換的問題。

(可以顯示轉換)比如透過 static_cast (c);

強型別:作用域列舉不會隱式轉換為整數或其他型別。

名稱空間隔離:列舉成員名在列舉型別的作用域內,避免了命名衝突。

顯式指定基礎型別:可以顯式指定列舉的基礎型別。

右值引用

左值是指可以被取地址的物件,表示某個記憶體位置。左值有持久的儲存,通常代表變數。

右值是指不能被取地址的臨時物件或常量,表示某個值本身而不是記憶體位置。

右值引用是對右值的引用,使用&&符號表示。右值引用只能繫結到右值。

尾置返回型別

auto functionName(parameters) -> returnType {
//函式體
}

// C++11中引入
// 使用尾置返回型別
auto add(int a, int b) -> int
// 模板函式,返回引數型別的乘積
template <typename T1, typename T2>
auto multiply(T1 a, T2 b) -> decltype(a * b)
// 使用尾置返回型別和 decltype 推導返回型別
template <typename Container>
auto get_first_element(Container& container) -> decltype(container[0])

final 和 override

override
當在父類中使用了虛擬函式時候,你可能需要在某個子類中對這個虛擬函式進行重寫,以下方法都可以:

xxx

如果不使用override,當你手一抖,將foo()寫成了f00()會怎麼樣呢?結果是編譯器並不會報錯,因為它並不知道你的目的是重寫虛擬函式,而是把它當成了新的函式。如果這個虛擬函式很重要的話,那就會對整個程式不利。所以,override的作用就出來了,它指定了子類的這個虛擬函式是重寫的父類的,如果你名字不小心打錯了的話,編譯器是不會編譯透過的。

final
當不希望某個類被繼承,或不希望某個虛擬函式被重寫,可以在類名和虛擬函式後新增final關鍵字,新增
final關鍵字後被繼承或重寫,編譯器會報錯。

程序和執行緒的概念

程序 是作業系統進行資源分配和排程的基本單位,是程式的一次執行過程。它擁有獨立的記憶體地址空間和系統資源。

執行緒 是程序中的一個執行流,是 CPU 排程和分派的基本單位,它可以與同屬一個程序的其他執行緒共享程序所擁有的全部資源。執行緒通常被用於執行程式碼,它們比程序更輕量級,建立和管理的開銷小。

為什麼要使用執行緒?

  1. 效率提升:多執行緒可以並行處理多個任務,提升程式的執行效率。
  2. 資源共享:執行緒間可以共享大部分程序資源,相較於程序間通訊,執行緒間通訊更簡單、快速。
  3. 響應速度提升:在多執行緒應用中,一個執行緒的長時間執行不會阻塞其他執行緒,提高應用的響應速度。

多執行緒環境中的死鎖

死鎖 是指多個執行緒在執行過程中因為競爭資源而造成的一種阻塞的現象,若無外力作用,這些執行緒都將無法向前推進。發生死鎖主要有四個必要條件:

  • 互斥條件:資源不能被多個執行緒共享。
  • 保持和等待:一個執行緒因請求資源而阻塞時,對已獲得的資源保持不放。
  • 非搶佔條件:執行緒已獲得的資源在未使用完之前,不能被其他執行緒強行搶佔。
  • 迴圈等待條件:存在一種執行緒資源的迴圈等待鏈,形成閉環。

執行緒安全

執行緒安全 指的是多執行緒環境中,程式或者程式碼在併發的情況下,能夠保證操作的正確性,不會因為多執行緒的排程和資源競爭導致資料出錯。

單例模式

單例模式 是一種設計模式,確保一個類只有一個例項,並提供一個全域性訪問點。單例模式常用於管理共享資源,如資料庫連線或檔案系統的操作。

實現單例模式的常見方法包括:

  • 懶漢模式:在第一次呼叫時初始化例項。
  • 餓漢模式:在程式啟動時建立例項。

在多執行緒環境下實現單例需要注意執行緒安全問題,常見的執行緒安全實現方法是使用同步機制,例如在獲取例項的方法上新增同步鎖。

程序的通訊方式

程序間通訊(IPC)是在不同程序之間交換資料和訊號的機制。主要的程序通訊方式包括:

  • 管道(Pipe):允許一個程序與另一個有親緣關係的程序進行通訊。
  • 命名管道(FIFO):與普通管道類似,但它們可以在無關的程序之間進行通訊。
  • 訊號(Signal):用於處理非同步事件。
  • 訊息佇列:訊息的連結串列,儲存在核心中,並由訊息佇列識別符號標識。
  • 共享記憶體:允許多個程序訪問同一塊記憶體空間。
  • 套接字(Socket):支援不同主機上的程序之間的通訊。

OSI七層模型

OSI(Open Systems Interconnection)模型 是一個參考模型,用於理解和設計網路的工作流程,分為七層:

  1. 物理層:處理物理裝置間的原始資料傳輸。
  2. 資料鏈路層:處理點對點的幀傳輸。
  3. 網路層:處理資料包從源到目的地的傳輸。
  4. 傳輸層:提供端到端的通訊服務。
  5. 會話層:管理網路中的會話。
  6. 表示層:確保資料在網路中流動的方式可以被髮送方和接收方理解。
  7. 應用層:為應用軟體提供服務。

TCP三次握手和四次揮手

TCP三次握手(建立連線):

  1. 客戶端傳送一個帶SYN標誌的資料包到伺服器。
  2. 伺服器回應一個帶SYN/ACK標誌的資料包以確認接收。
  3. 客戶端傳送ACK包回伺服器,確認連線建立。

TCP四次揮手(斷開連線):

  1. 客戶端或伺服器發起關閉,傳送FIN包。
  2. 接收方確認這個FIN包,回送一個ACK包。
  3. 接收方傳送一個FIN包,表示同意關閉連線。
  4. 發起關閉的一方確認FIN包,回送ACK包,完成斷開。

四次揮手​​​​​​​過程詳細說明:

1、客戶端傳送斷開TCP連線請求的報文,其中報文中包含seq序列號,是由傳送端隨機生成的,並且還將報文中的FIN欄位置為1,表示需要斷開TCP連線。(FIN=1,seq=x,x由客戶端隨機生成);

2、服務端會回覆客戶端傳送的TCP斷開請求報文,其包含seq序列號,是由回覆端隨機生成的,而且會產生ACK欄位,ACK欄位數值是在客戶端發過來的seq序列號基礎上加1進行回覆,以便客戶端收到資訊時,知曉自己的TCP斷開請求已經得到驗證。(FIN=1,ACK=x+1,seq=y,y由服務端隨機生成);

3、服務端在回覆完客戶端的TCP斷開請求後,不會馬上進行TCP連線的斷開,服務端會先確保斷開前,所有傳輸到A的資料是否已經傳輸完畢,一旦確認傳輸資料完畢,就會將回復報文的FIN欄位置1,並且產生隨機seq序列號。(FIN=1,ACK=x+1,seq=z,z由服務端隨機生成);

4、客戶端收到服務端的TCP斷開請求後,會回覆服務端的斷開請求,包含隨機生成的seq欄位和ACK欄位,ACK欄位會在服務端的TCP斷開請求的seq基礎上加1,從而完成服務端請求的驗證回覆。(FIN=1,ACK=z+1,seq=h,h為客戶端隨機生成)
至此TCP斷開的4次揮手過程完畢。

右值引用

右值引用是C++11中引入的一個特性,它允許開發者引用臨時物件。右值引用使用 && 符號標記,與傳統的左值引用(使用 &)不同。它主要用於實現移動語義和完美轉發。透過移動語義,可以將資源(如動態分配的記憶體)從一個物件轉移到另一個物件,這樣可以避免不必要的複製,提高效能。

什麼情況下會應用右值引用
右值引用是C++11引入的一項功能,它主要用於兩個場景:提升效能和實現移動語義以及完美轉發。以下是使用右值引用的一些具體情景:

1. 移動語義

右值引用允許開發者區分哪些物件是可以安全地“移動”的。這種特性尤其有用於大物件或資源管理密集型的物件(如大型陣列、字串、檔案控制代碼、網路連線等)。使用移動語義可以避免昂貴的深複製操作,而是透過轉移所有權(如指標和控制代碼)從一個物件到另一個物件來顯著提高效能。

std::vector<int> createHugeVector() {
    std::vector<int> v(1000000, 42);  // 建立一個大型vector
    return v;  // 返回vector,觸發移動構造,而非複製構造
}

void useVector() {
    std::vector<int> myVec = createHugeVector();  // 使用移動構造
}

在上面的例子中,當從函式 createHugeVector 返回 vector 時,使用移動建構函式而不是複製建構函式,因為返回的是一個臨時物件(右值)。

2. 完美轉發

右值引用結合模板和 std::forward 函式可以用於實現完美轉發,這允許函式模板精確地保持其接收到的實參的型別(包括其值類別,即左值或右值)。

template<typename T>
void forwarder(T&& arg) {
    someFunction(std::forward<T>(arg));  // 轉發arg到someFunction
}

void someFunction(int& x) {
    std::cout << "Lvalue function\n";
}

void someFunction(int&& x) {
    std::cout << "Rvalue function\n";
}

int main() {
    int x = 10;
    forwarder(x);  // 呼叫的是someFunction(int&)
    forwarder(5);  // 呼叫的是someFunction(int&&)
}

這裡,forwarder 函式模板可以完美地轉發它的引數到 someFunction,根據實參的原始型別(左值還是右值)選擇合適的函式版本。

3. 資源所有權的管理

右值引用常用於智慧指標,如 std::unique_ptr,它需要在物件間轉移所有權時使用移動語義。

std::unique_ptr<int> source = std::make_unique<int>(10);
std::unique_ptr<int> destination = std::move(source);  // 明確轉移所有權

在此例中,使用 std::movesource 中的所有權轉移到 destination。這是必須的,因為 std::unique_ptr 不允許複製。

右值引用和相關技術提供了強大的工具來最佳化程式效能,特別是在處理大型資料或資源密集型物件時。這些技術也使得C++程式更加高效和靈活。

深複製與淺複製

  • 淺複製:僅複製物件的指標,不復制指標所指向的資料。兩個物件的指標指向同一塊記憶體區域,修改一個物件的資料可能會影響到另一個物件。
  • 深複製:複製物件及其所有的資料。通常需要顯式地在複製建構函式和賦值運算子中實現。深複製確保複製的物件擁有一份獨立的資料副本。

構造的列表初始化和括號內初始化

在C++11及以後的版本中,初始化的方式有所擴充套件,包括列表初始化和傳統的建構函式初始化:

  • 列表初始化(使用花括號 {}):提供了一種統一的初始化語法,用於初始化任何型別的物件,包括陣列、容器和基本資料型別。列表初始化還防止了資料丟失,因為如果初始化時發生了窄化轉換,編譯器將報錯。
  • 括號內初始化(使用圓括號 ()):這是傳統C++的初始化方式,適用於大多數型別,但在某些情況下可能會與函式宣告產生歧義。

行內函數

行內函數是一種最佳化技術,用於減小函式呼叫的開銷。透過在函式宣告前加上 inline 關鍵字,編譯器被建議在每個呼叫點上將函式體直接展開,而不是進行函式呼叫。這通常用於小規模的、頻繁呼叫的函式。

模板

模板是C++中支援泛型程式設計的一種機制,它允許程式設計師編寫與型別無關的程式碼。模板可以用於建立泛型函式或資料結構。有兩種基本形式的模板:

  • 函式模板:用於建立可接受任何資料型別的函式。
  • 類别範本:用於建立可以儲存任何資料型別的類。