New features of C++11 ① | Detailed explanation of common keywords in C++11

Table of contents

1 Introduction

2. Detailed explanation of new keywords in C++11

2.1、auto

2.2、override

2.3、final

2.4、nullptr

2.5. Use =delete to prevent copying objects

2.6、decltype

2.7、noexcept

2.8、constexpr

2.9、static_assert


VC++ common function development summary (column article list, welcome to subscribe, continuous update...) icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124272585 C++ software exception troubleshooting series tutorial from entry to mastery (column article list , Welcome to subscribe, keep updating...) icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931 C++ software analysis tools from entry to mastery case collection (column article is being updated...) icon-default.png?t=N7T8https:/ /blog.csdn.net/chenlycly/article/details/131405795 C/C++ basics and advanced (column articles, continuously updated...) icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.html        C++ 11 New features are very important. As a C++ developer, it is necessary to learn. Not only will it be involved in the written test interview, but it will be used on a large scale in open source code. Take the open source WebRTC project used by many video conferencing and live broadcast software as an example. The new features of C++11 and above are widely used in the WebRTC code. To understand its source code, you must understand these new features of C++. Therefore, in the next period of time, I will combine my work practice to explain the new features of C++11 in detail for reference or reference.

1 Introduction

       In order to improve the flexibility and efficiency of the C++ language, C++11 introduces multiple keywords, such as auto, overide, final, nullptr, decltype, constexpr, noexcept, static_assert, etc. This article combines coding practice, mainly introduces the commonly used new keywords introduced in C++11.

The new standards of C++11 and above introduce many new features, which make C++ more flexible, but also make the features of C++ more bloated and make C++ more difficult to control.

2. Detailed explanation of new keywords in C++11

2.1、auto

       When programming, it is often necessary to assign the value of an expression to a variable, which requires clearly indicating the type of the expression when declaring the variable, but sometimes it is difficult to determine. The C++11 standard introduces the auto type specifier, which allows the compiler to analyze the type of the expression for us. The auto variable must be initialized, because the compiler needs to deduce the type of the auto variable through the initial value.

       Automatic type deduction, used to deduce the data type of the variable from the initialization expression (actually, the type deduction is performed on the variable at compile time, so it will not adversely affect the running efficiency of the program). Examples are as follows:

auto i = 2;      // int类型
auto d = 1.0;  //  double类型
auto str = "hello word"; // const char*
auto ch =  'A';   // char类型
auto func = less<int>();  // 函数指针
vector<int> vtList; 
auto iter = vtList.begin();  // 选代器类型
auto p = new foo();         // 自定义类型 

2.2、override

       When the derived class rewrites the virtual function of the base class, it can add a virtaul mark before the function, so that we know that the function is a virtual function rewriting the base class after seeing this mark. After C++11 introduced the override keyword, we can use override to more clearly identify the rewritten function.

       The advantage of adding the override flag to the function of the derived class is that on the one hand, it makes the programmer's intention to rewrite the base class clearer, and on the other hand, it allows the compiler to find some errors. If we use override to mark a certain function of the derived class, but If the function does not override the virtual function of the base class, the compiler will report an error.

class Base
{
public:
    virtual void func() const
    {
        cout << __func__ << std::endl;
    }
}

class Derived :public Base
{
public:
    virtual void func() overide
    {
        cout << __func__ << std::endl;
    }
}

2.3、final

       Sometimes we need to define such a class that we don't want other classes to inherit from it. Or don't want to think about whether it's suitable as a base class. In order to achieve this goal, C++11 introduces a keyword final to prevent inheritance. Putting this keyword after the class name means that the class cannot be inherited. Examples are as follows:

class Base{ /*   */};
class Last final : public Base { /*   */};

        You can also use this keyword to modify a member function of a class to prevent the function from being overridden by derived classes:

class Base
{
public:
    virtual void func() const
    {
        cout << __func__ << std::endl;
    }
}

class Derived :public Base
{
public:
    virtual void func() overide final
    {
        cout << __func__ << std::endl;
    }
}

2.4、nullptr

       Nullptr is a new type introduced to solve the ambiguity of NULL in C++, because NULL actually represents 0. It is best to use the literal value nullptr to initialize a pointer, indicating that it is currently a null pointer. nullptr is a special type of literal that can be converted to any other pointer type.

       Why is it said that nullptr can resolve the ambiguity of NULL? You can see an example:

void func( int );
void func( int* );

For example, in the above two overloaded functions, we need to call the func function. If NULL is passed in (NULL actually represents 0, it can be implicitly converted to void*, and then converted to int*), both functions can be adjusted. Go in, the compiler doesn't know which one to call, which creates ambiguity, and an error will be reported when compiling. When the nullptr parameter is passed, the compilation will not report an error, and it will be clearer that the void func(int*) is called.    

       nullptr_t is a variable type, its value is nullptr, you can see the definition of nullptr_t:

#ifdef __cplusplus
    namespace std
    {
        typedef decltype(__nullptr) nullptr_t;  // nullptr_t被声明为__nullptr的类型
    }

    using ::std::nullptr_t;
#endif

If there is nullptr_t in the function parameter, you don’t need to specify the variable name, just set the corresponding parameter in the function body to nullptr. Take a certain constructor of the shared_ptr smart pointer as an example: (the code comes from the source code implementation of the smart pointer in Visual C++)

template<class _Dx,
    class _Alloc,
    enable_if_t<conjunction_v<is_move_constructible<_Dx>,
    _Can_call_function_object<_Dx&, nullptr_t&>
               >, int> = 0>
shared_ptr(nullptr_t, _Dx _Dt, _Alloc _Ax)  // 参数类型为nullptr_t,对应的值就是nullptr
{    // construct with nullptr, deleter, allocator
    Setpda(nullptr, _STD move(_Dt), _Ax);
}    

2.5. Use =delete to prevent copying objects

       In fact, =delete in this place is not a keyword!

       In the new C++11 standard, we prevent copying by defining the copy constructor and copy copy function as deleted functions. Add =delete after the function, and the function becomes a deleted function. For the deleted function, although we declare it, we cannot use it in any way.

       Take the smart pointer class std::unique_ptr as an example. This smart pointer class does not support copy construction and assignment operations, but mainly supports ownership transfer operations. So in the definition of the class, set the copy constructor and assignment function of the class to the =delete function to prevent copy construction and assignment: (This is a classic interview question: How does the std::unique_ptr smart pointer prohibit copying and copied?)

unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;

We can't achieve this goal by not implementing the copy constructor and assignment function, because when we don't implement these two functions, the compiler will automatically generate the default copy constructor and assignment function for us.

       Before there is no C++ =delete flag, the copy constructor and assignment function can be set as private, so that these two functions cannot be used outside the class.

2.6、decltype

       Sometimes you want to infer the type of the variable to be defined by the type of the expression, but you don't want to use the value of the expression to initialize the variable to infer the type of the variable. In order to meet this requirement, the C++11 standard introduces the decltype type indicator , its role is to infer the data type of the operand and return it. The compiler only analyzes the type of the inferred expression, but does not calculate the value of the expression (does not evaluate the expression).

const int nVal = 0;
decltype(nVal) y = 1;
decltype(t + u); // 其中t和u是数据类型,不对表达式t+u进行求值,只去推断t+u表达式的数据类型。

       The type specifier generates the type of the specified expression, and the type is deduced according to the type of the expression at compile time. Examples are as follows:

int i;
struct A
{
    double x; 
};
const A* a = new A();

decltype(i)            x2; // int
dec1type (a->x)       x3; // double
dec1type((a->x))       x4;   // double&

2.7、noexcept

       In the C++11 standard, the noexcept keyword is provided to specify that a function does not throw exceptions. Put this keyword after the parameter list of the function, as follows:

void func() noexcept;            // 这里noexcept作为修饰符

It is beneficial for the user and the compiler to know in advance that a function does not throw exceptions. First, knowing that a function doesn't throw exceptions helps simplify the code that calls that function; second, if the compiler confirms that a function doesn't throw exceptions, it can perform some special optimizations that don't apply to Possible error code.

       There may be a situation where although the function is declared not to throw an exception using the noexcept keyword, an exception is actually thrown inside the function. Once a noexcept function throws an exception, the program will call std::terminate() to terminate the program to ensure the promise of not throwing an exception at runtime.

       Indicate that a function does not throw an exception, so that the caller does not have to think about how to handle the exception. Whether the function does not throw an exception, or is forcibly terminated after throwing an exception, the caller does not need to be responsible for it.

       In C++98, throw() is used to declare that no exception is thrown, and throw(exception type) declares the type of exception that may be thrown. noexcept is more efficient than throw, because the compiler can use std::terminate() to terminate the program, and the throw exception mechanism will have some additional overhead, such as the function stack is expanded in turn and the automatic variables are destructed.

       When the memory of the program process is insufficient, the new operation will throw a bad_alloc exception, and the memory allocation fails. We have mentioned this problem before. The solution is: pass a std::nothrow parameter when new, so that new cannot apply for memory Do not throw an exception, return NULL directly, so that we can judge whether the memory application failed by whether the returned address is NULL (empty), the code is as follows:

#include <iostream>
 
int main(){
    char *p = NULL;
    int i = 0;
    do{
        p = new(std::nothrow) char[10*1024*1024]; // 每次申请10MB
        i++;
        
        Sleep(5);
    }
    while(p);
 
    if(NULL == p){
        std::cout << "分配了 " << (i-1)*10 << " M内存"         //分配了 1890 Mn内存第 1891 次内存分配失败           
                  << "第 " << i << " 次内存分配失败";
 
    }
    return 0;
}

Of course, when the memory of the program process is insufficient, the business cannot be launched and executed normally, and it does not make much sense to keep the program alive. 

2.8、constexpr

       Refers to an expression whose value does not change and whose result can be obtained during compilation. The new C++11 standard stipulates that variables are allowed to be declared as constexpr types so that the compiler can verify whether the variable is a constant expression. A variable declared constexpr must be a constant and must be initialized with a constant expression.

constexpr int mf= 20;           // 20 是常量表达式
constexor int limit = mf + 1;   // mf + 1 是常量表达式
constexor int sz = size();         // 只有当size是一个constexpr函数时才是一条正确的语句

       constexpr can also modify functions. Once a function is declared as constexpr, the return value type of the function and the types of all formal parameters must be literal value types, and there can only be one retrun statement in the function body.

constexpr int new_sz(){ retrun 20; };

2.9、static_assert

       static_assert is used to make assertions during compilation, so it is called a static assertion. The syntax is as follows:

static_assert(constant expression, prompt string)

for example:

static_assert(sizeof(int) < sizeof(unsigned int), "int is not smaller than unsigned int");

If the value of the first parameter constant expression is true (true or non-zero value), then static_assert does nothing, as if it does not exist, otherwise a compilation error will be generated, and the error location is the line where the static_assert statement is located. The error prompt is the prompt string of the second parameter.

       Using static_assert, we can find more errors during compilation, enforce some contracts with the compiler, and help us improve the readability of compilation messages.

Guess you like

Origin blog.csdn.net/chenlycly/article/details/132701306