Cplusplus 简明教程

Advanced C++ Concepts

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

C is a one of the foundation languages of modern programming. It has evolved out of the basic C into a very powerful tool in modern programming. The versions of C started with C 98, and are now upto C 20. After the update of C 11, all modern updates are known collectively as modern C. These new models have vast and new features, making the language more user- friendly and better feature-equipped. Some of these new concepts were already part of other new languages like Ethereum, Ruby, Python, and Javascript, and with the introduction of these concepts in C++, programming has become more efficient today.

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

Here is a list of different Advanced C++ topics we are going to understand in detail −

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

With the version of C 20, other features are also available, which are a slightly more advanced and would be covered in the later parts of this post. The features mentioned above are far advanced concepts as well, but the explanation provided in this post shall be adequate for readers to deep dive into the MODERN C LANGUAGE.

RAII (Resource Aquisition is Initialization)

raii

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

Resource Acquisition is Initialization, often referred to by its acronym RAII, is a C technique which is used for memory management. Although it’s association with C is typically why it is studied, the scope of RAII extends beyond the barriers of language restrictions.

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

To simply put up a definition, RAII means assigning memory to an object in form of a constructor, and then releasing the assigned memory using a destructor. Hence, it forms a part of OOP concepts, which was covered in the past topics.

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

Now, you must be curious to know what problems does RAII actually solve? RAII works in many ways, some of which are −

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

Some of these topics have already been discussed in the previous parts of this section, and some new concepts are discussed in the later parts of this post.

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

Now, what is actually a resource in programming, particularly in terms of OOPS?

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

A resource is an entity that can be required during the compilation or execution of a program or a sequence of programs. Examples of resources are Stack, Heap, Memory, Files, Sockets (in socket programming), Locks and Semaphores, etc. These resources are crucial for the smooth working of a program. These are acquired by the program through requests, like mutex() method calls for a mutex lock to be acquired.

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

In classical programming using C, we use concepts of new() and delete() to create entities and then deallocate memory. This traditional concept, while still acceptable in OOP languages like C, is however, discouraged. In C, the concept of RAII makes it easy for allocation and deallocation of resources within a scope.

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

The tenure of a new quantity is the tenure of the object, and while a constructor can create and assign memory to an object, a destructor can be used to simply release the memory after completion automatically. This makes C++ a very efficient and user-friendly language. Let’s understand this with a simple example.

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++

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

A pointer is called a wild pointer if it points randomly to any address in the memory. This happens when the pointer is declared in the program, but it is not initialized to point to an address value. Wild pointers are different from normal pointers i.e. they also store the memory addresses but point the unallocated memory or data value which has been deallocated.

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

These pointers can cause memory leak, a topic that will be discussed in the later parts of this article.

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 指针会引发错误。

In earlier versions of C++, the NULL would be defined as a void element which points to no memory. The conversion of NULL to int or similar data types was allowed, but in case of overloading of functions, the NULL pointer throws error.

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

Since the emergence of C++ 11, the NULL was redefined to nullptr, which is a special data type that can only be used as a pointer to point to an address that is not available in the memory.

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

Hence, it can act as a pointer to any location on redefining the pointer variable. Unlike NULL, it is not implicitly convertible or comparable to integral types, like int or char. Hence, it solves the problem of NULL invariably.

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

On a side note, comparison between null pointers is possible in the newer version of C++, and hence it can be comprehended that pointers are comparable to bool data type.

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 ,或错误地使用它,则可能会发生这种情况。

Memory leakage is a major problem in many computing devices, as the stack and heap memory available with the compiler in a program is limited and very costly. Memory leakage occurs when new objects are declared, used and not cleared out of the memory. This can happen if programmers forget to use the delete operation, or use it incorrectly.

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

There are huge disadvantages with memory leakage, as the space is exponentially increasing with each incoming process request, and new processes have to be allocated new memory spaces instead of clearing unrequired memory.

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

The given program illustrates how memory leak occurs in a program using 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() 对象的内存释放掉来避免。以下程序说明了如何避免内存泄漏。

This can be avoided by deallocating the memory that was allocated in the first place to the new() object. The following program illustrates how memory leakage can be avoided.

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 中引入。其中一个包装器类是智能指针,它有助于确保没有内存泄漏和错误实例。

With the introduction of RAII and OOP concepts in C, wrapper classes have also been introduced in C. One of these wrapper classes is the Smart Pointer, which help to make sure there are no instances of memory leaks and errors.

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 ,该表达式用于较小的代码行,而无需为函数提供名称和作用域。

Since C 11, the use of lambda expression has been allowed in C to resolve inline functions which are used for small lines of code without the need to give the function a name and a scope.

Syntax

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

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

Here, the return type is resolved by the compiler itself, and there is no need to specify the return type of the function. However, in case of complex statements, the return type is specified for the compiler to run properly.

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

External variables can be captured in the following ways −

  1. Capture by reference

  2. Capture by value

  3. Capture by both (mixed capture)

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

Syntax used for capturing variables is given as −

  1. [&] : capture all external variables by reference

  2. [=] : capture all external variables by value

  3. [a, &b] : capture a by value and b by reference

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