高階 C++ 概念



C++ 是現代程式設計的基礎語言之一。它從基本的 C 語言演變而來,成為現代程式設計中非常強大的工具。C++ 的版本從 C++ 98 開始,現在已經發展到 C++ 20。在 C++ 11 更新之後,所有現代更新都被統稱為現代 C++。這些新模型具有廣泛的新功能,使該語言更易於使用者使用,並具有更好的功能。其中一些新概念已經是其他新語言的一部分,例如 以太坊RubyPythonJavascript,隨著這些概念在 C++ 中的引入,如今的程式設計效率更高。

以下是我們將詳細瞭解的不同高階 C++ 主題的列表:

隨著C++ 20版本的釋出,還提供了其他功能,這些功能稍微高階一些,將在本文的後面部分介紹。上面提到的功能也是非常高階的概念,但是本文提供的解釋足以讓讀者深入研究現代 C++ 語言

RAII(資源獲取即初始化)

RAII

資源獲取即初始化,通常用其縮寫 RAII 指代,是一種用於記憶體管理的 C++ 技術。雖然它通常與 C++ 相關聯,這就是為什麼它被研究的原因,但 RAII 的範圍超出了語言限制的障礙。

簡單地說,RAII 意味著以建構函式的形式將記憶體分配給物件,然後使用解構函式釋放分配的記憶體。因此,它構成了OOP 概念的一部分,這在之前的主題中已經介紹過。

現在,您一定很好奇 RAII 到底解決了哪些問題?RAII 以多種方式工作,其中一些是:

這些主題中的一些已經在本文的前面部分討論過,一些新概念將在本文的後面部分討論。

現在,在程式設計中,尤其是在面向物件程式設計方面,資源究竟是什麼?

資源是在編譯或執行程式或一系列程式期間可能需要的實體。資源的示例包括記憶體檔案套接字(在套接字程式設計中)、鎖和訊號量等。這些資源對於程式的順利執行至關重要。程式透過請求獲取這些資源,例如,對於互斥鎖,使用 mutex() 方法呼叫來獲取。

在使用 C 的經典程式設計中,我們使用 new() 和 delete() 的概念來建立實體,然後釋放記憶體。這種傳統概念在 C++ 等 OOP 語言中仍然可以接受,但是不鼓勵使用。在 C++ 中,RAII 的概念使得在作用域內輕鬆分配和釋放資源成為可能。

新數量的有效期是物件的有效期,建構函式可以建立和分配記憶體給物件,而解構函式可以用來在完成後自動釋放記憶體。這使得 C++ 成為一種非常高效且使用者友好的語言。讓我們透過一個簡單的例子來理解這一點。

示例

#include <bits/stdc++.h>
using namespace std;

mutex m;

void bad() {
   m.lock();             // acquire the mutex
   f();                  // if f() throws an exception, the mutex is never released
   if (!everything_ok())
      return;           // early return, the mutex is never released
   m.unlock();           // if bad() reaches this statement, the mutex is released
}
 
void good(){
   lock_guard<mutex> lk(m);      // RAII class: mutex acquisition is initialization
   f();                                      // if f() throws an exception, the mutex is released
   if (!everything_ok())
     return;                           // early return, the mutex is released
} 

int main(){
   good();
   bad();
   return 0;
}

C++ 中的野指標

如果指標隨機指向記憶體中的任何地址,則該指標稱為野指標。當在程式中宣告指標但未將其初始化為指向地址值時,就會發生這種情況。野指標與普通指標不同,即它們也儲存記憶體地址,但指向未分配的記憶體或已釋放的資料值。

這些指標可能導致記憶體洩漏,這將在本文的後面部分討論。

示例

#include <bits/stdc++.h>
using namespace std;
int main() {
   int *ptr;
   //this pointer has been declared but not initialized
   //hence, it is a wild pointer
   cout<<*ptr<<endl; 

   int a=11;
   ptr=&a;
   cout<<*ptr<<endl<<ptr<<endl;

   //once a value is declared, it becomes a normal pointer
   *ptr=10;
   cout<<*ptr<<endl<<ptr; 

   return 0;
}

輸出

-660944088
11
0x7ffcfb77825c
10
0x7ffcfb77825c

C++ 中的空指標

在早期版本的 C++ 中,NULL 將被定義為指向任何記憶體的 void 元素。允許將 NULL 轉換為int 或類似的資料型別,但在函式過載的情況下,空指標會引發錯誤。

自從C++ 11出現以來,NULL 已重新定義為nullptr,這是一種特殊的資料型別,只能用作指標來指向記憶體中不可用的地址。

因此,它可以在重新定義指標變數時充當指向任何位置的指標。與 NULL 不同,它不能隱式轉換為整數型別(如intchar),也不能與整數型別進行比較。因此,它解決了 NULL 的固有問題。

順便說一句,在新版本的 C++ 中,空指標之間可以進行比較,因此可以理解指標可以與bool資料型別進行比較。

示例

#include <bits/stdc++.h>
using namespace std;

int main() {

   //int ptr=nullptr;

   //this throws compiler error as it is not comparable to int
   //run the above line for illustration

   int *ptr=nullptr;

   if(ptr==nullptr) cout<<"true";
   else cout<<"false";

   return 0;
}

輸出

true

C++ 中的記憶體洩漏

記憶體洩漏是許多計算裝置中的一個主要問題,因為編譯器在程式中可用的記憶體是有限的且非常昂貴。當宣告新物件、使用新物件但未清除記憶體中的新物件時,就會發生記憶體洩漏。如果程式設計師忘記使用delete 操作或錯誤地使用它,就會發生這種情況。

記憶體洩漏有很大的缺點,因為空間隨著每個傳入的程序請求呈指數增長,新的程序必須分配新的記憶體空間,而不是清除不需要的記憶體。

給定的程式演示瞭如何在使用 C++ 的程式中發生記憶體洩漏。

示例

#include <bits/stdc++.h>
using namespace std;

void leak_func(){
   int* p = new int(10);
   //using new() to declare a new object

   //no delete() operation
   return;
}

int main(){
   leak_func();

   return 0;
}

可以透過釋放最初分配給new()物件的記憶體來避免這種情況。下面的程式說明了如何避免記憶體洩漏。

示例

#include <bits/stdc++.h>
using namespace std;

void leak_func(){
   int* p = new int(10);
   //using new() to declare a new object

   delete(p);
   return;
}

int main(){
   leak_func();

   return 0;
}

C++中的智慧指標

隨著C++中RAII和OOP概念的引入,包裝類也已在C++中引入。這些包裝類之一是智慧指標,它有助於確保沒有記憶體洩漏和錯誤的例項。

示例

#include <bits/stdc++.h>
using namespace std;

int main() {

   //int ptr=nullptr;

   //this throws compiler error as it is not comparable to int

   int *ptr=nullptr;

   if(ptr==nullptr) cout<<"true";
   else cout<<"false";

   return 0;
}

輸出

true

C++中的Lambda表示式

從C++ 11開始,允許在C++中使用lambda表示式來解決行內函數,這些函式用於少量程式碼行,無需為函式命名和指定作用域。

語法

[ capture clause ] (parameters) -> return-type{   
   definition of method   
}

在這裡,返回型別由編譯器本身解析,無需指定函式的返回型別。但是,對於複雜的語句,需要指定返回型別才能使編譯器正常執行。

可以按以下方式捕獲外部變數:

  • 按引用捕獲
  • 按值捕獲
  • 同時捕獲(混合捕獲)

用於捕獲變數的語法如下:

  • [&]:按引用捕獲所有外部變數
  • [=]:按值捕獲所有外部變數
  • [a, &b]:按值捕獲a,按引用捕獲b

示例

#include <bits/stdc++.h>
using namespace std;

void printvector(vector<int> &v){
   // lambda expression to print vector
   for_each(v.begin(), v.end(), [](int i){
      std::cout << i << " ";
   });
   cout << endl;
}

int main(){
   vector<int> v;
   v.push_back(10);

   v.push_back(11);

   v.push_back(12);
   printvector(v);

   return 0;
}

輸出

10 11 12 
廣告