Cplusplus 简明教程

Advanced C++ Concepts

C 是现代编程的基本语言之一。它已从基本 C 发展为现代编程中非常强大的工具。C 的版本从 C 98 开始,现已升级至 C 20 。在 C 11 更新后,所有现代更新被统称为现代 C。这些新模型有大量新功能,使该语言更友好和具有更好的功能。其中一些新概念已成为其他新语言的部分,如 EthereumRubyPythonJavascript ,而随着这些概念在 C++ 中的引入,现在的编程变得更高效。

我们将在详细了解以下不同的高级 C++ 主题:

C 20 版本,还提供了其他功能,这些功能稍微更高级,将在本帖的后面部分讨论。上述功能也是非常高级的概念,但本帖所提供的说明足以使读者深入了解 MODERN C LANGUAGE

RAII (Resource Aquisition is Initialization)

raii

Resource Acquisition is Initialization (通常用其缩写 RAII 指代)是用于执行 memory management 的 C 技术。虽然它与 C 的关联通常是学习它的原因,但 RAII 的范围超越了语言限制的障碍。

简单地提出一个定义,RAII 表示以构造函数的形式向对象分配内存,然后使用 destructor 释放已分配的内存。因此,它构成了我们在过去主题中讨论过的 OOP concepts 的一部分。

现在,你一定很好奇 RAII 实际上解决了哪些问题?RAII 有很多种作用,其中一些如下:

本部分中已讨论了其中一些主题,而新的概念将在本帖的后面部分讨论。

现在,在编程中 resource 实际上是什么,特别是在面向对象的编程(OOPS)中?

资源是一个实体,可以在程序或一系列程序的编译或执行中进行要求。资源的示例包括 StackHeapMemoryFilesSockets (在套接字编程中)、 Locks and Semaphores 等。这些资源对程序的顺利运行至关重要。这些资源通过程序通过请求获取,例如用于获取互斥锁的 mutex() 方法调用。

在使用 C 的经典编程中,我们使用 new() 和 delete() 的概念来创建实体,然后释放内存。这个传统概念虽然在 C 这样的面向对象语言中仍然可以接受,但是,不鼓励使用。在 C 中,RAII 的概念使得在作用域内分配和释放资源变得容易。

新的数量的任期就是对象的任期,并且构造函数可以为对象创建和分配内存,而析构函数可用于仅仅在自动完成之后释放内存。这使得 C++ 成为一种非常高效且用户友好的语言。我们用一个简单的示例来理解这一点。

Example

#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;
}

Wild Pointers in C++

如果指针随机指向内存中的任何地址,则该指针称为野指针。当指针在程序中声明时,但未初始化为指向地址值时,就会发生这种情况。野指针不同于普通指针,即它们还存储内存地址,但指向未分配的内存或已被释放的数据值。

这些指针可能导致 memory leak ,这将在本文的后面部分讨论。

Example

#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

Null Pointers in C++

在 C++ 的早期版本中, NULL 将定义为无意义的元素,它不指向任何内存。允许将 NULL 转换为 int 或类似数据类型,但对于函数的 overloading ,NULL 指针会引发错误。

自从 C++ 11 出现以来,NULL 已重新定义为 nullptr ,它是一种特殊数据类型,只能用作指向内存中不可用地址的指针。

因此,它可以在重新定义指针变量时充当指向任何位置的指针。与 NULL 不同,它不能隐式转换为整数类型,例如 intchar ,也不能与整数类型进行比较。因此,它根本解决了 NULL 的问题。

顺便提一下,在 C++ 的较新版本中可以比较空指针,因此可以理解指针可以与 bool 数据类型进行比较。

Example

#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

Memory Leakage in C++

内存泄漏是许多计算设备中的一个主要问题,因为编译器在程序中可用的 stackheap 内存是有限的且非常昂贵的。当声明新对象、使用新对象并且未从内存中清除新对象时,就会发生内存泄漏。如果程序员忘记使用 delete operation ,或错误地使用它,则可能会发生这种情况。

内存泄漏有很大的缺点,因为随着每个传入进程的请求,空间会呈指数级增加,并且必须为新进程分配新的内存空间,而不是清除不需要的内存。

给定的程序说明了如何使用 C++ 中的程序中发生内存泄漏。

Example

#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() 对象的内存释放掉来避免。以下程序说明了如何避免内存泄漏。

Example

#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;
}

Smart Pointers in C++

随着 RAII 和 OOP 概念在 C 中的引入, wrapper classes 也已在 C 中引入。其中一个包装器类是智能指针,它有助于确保没有内存泄漏和错误实例。

Example

#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

Lambda Expression in C++

自 C 11 以来,允许在 C 中使用 lambda 表达式来解析 inline functions ,该表达式用于较小的代码行,而无需为函数提供名称和作用域。

Syntax

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

在此,返回类型由编译器本身解析,无需指定函数的 return type 。但是,对于复杂语句,指定了返回类型以使编译器正常运行。

可以以下列方式捕获外部变量:

  1. Capture by reference

  2. Capture by value

  3. 同时捕获(混合捕获)

用于捕获变量的语法如下 -

  1. [&]:引用捕获所有外部变量

  2. [=]:值捕获所有外部变量

  3. [a,&b]:值捕获 a 并引用捕获 b

Example

#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