Smart pointer
unique_ptr
unique_ptr is one of the simplest and easiest to use smart pointers. When declaring, you must specify the type with template parameters, for example:
unique_ptr<int> ptr1(new int(10)); //int smart pointer assert(*ptr1 = 10); // Use * to get content assert(ptr1 != nullptr); //You can determine whether it is a null pointer unique_ptr<string> ptr2(new string("hello")); //string smart pointer assert(*ptr2 = "hello"); // You can use * to get content assert(ptr2->size() == 5); //You can use '>' to call member functions
Need special attention, unique_ Although PTR is called pointer and is used like pointer, it is not a pointer but an object. So don't try to be unique_ptr calls delete, which will automatically manage the initialized address and release resources when leaving the scope.
In addition, unique_ptr does not define addition and subtraction operations and cannot move the pointer address at will, which completely avoids dangerous operations such as pointer crossing and makes the code safer:
ptr1++; //Cause compilation errors ptr2 += 2; //Cause compilation errors
Using the factory function make_unique to create unique_ptr pointer, make_unique is defined in C++14.
auto ptr3 = make_unique<int>(42); assert(ptr3 && *ptr3 == 42); auto ptr4 = make_unique<string>("Hello C++"); // Call the factory function to create a smart pointer assert(!ptr4->empty());
If we use C++11, we can refer to the following code to implement a simplified version of make_unique():
template<class T, class... Args> //Variable parameter template std::unique_ptr<T> my_make_unique(Args&&... args) //Entry parameters of variable parameter template { return std::unique_ptr<T>( //Construct smart pointer new T(std::forward<Args>(args)...)); //"Perfect forwarding" }
unique_ The ownership of PTR pointer is unique and cannot be shared. Only one "person" can hold it at any time.
To achieve this, unique_ptr applies C + + transfer semantics and prohibits copy assignment, so it is transferring to another unique_ When assigning PTR, it should be noted that the ownership transfer must be explicitly declared with std::move(). After the assignment operation, the ownership is transferred, the original unique_ptr becomes null pointer, new unique_ptr takes over the ownership, thus ensuring the uniqueness of ownership:
auto ptr1 = make_unique<int>(42); //Call the factory function to create a smart pointer assert(ptr1 && *ptr1 == 42); //Smart pointer valid auto ptr2 = std::move(ptr1); //Use move() to transfer ownership assert(!ptr1 && ptr2); //ptr1 becomes a null pointer
Because unique_ptr prohibits copying and can only be transferred, so if you define a class, it will be unique_ptr as a member, then the class itself is not replicable. That is, unique_ptr will pass its "sole ownership" feature to its holder.
Shared pointer
shared_ PTR (shared pointer) is a better than unique_ptr is a smarter pointer.
shared_ptr<int> ptr1(new int(10)); //int smart pointer assert(*ptr = 10); //At this time, the smart pointer is valid shared_ptr<string> ptr2(new string("hello")); //string smart pointer assert(*ptr2 == "hello"); auto ptr3 = make_shared<int>(42); //Call the factory function to create a smart pointer assert(ptr3 && *ptr3 = 42); auto ptr4 = make_shared<string>("zelda"); //Call the factory function to create a smart pointer assert(!ptr4->empty()); //Member functions can be called with '>'
Its ownership can be safely shared, that is, it supports copy assignment and can be held by multiple people at the same time, just like the original pointer.
auto ptr1 = make_shared<int>(42); //Call the factory function to create a smart pointer assert(ptr1 && ptr1.use_count() == 1); //At this time, the smart pointer is valid and unique auto ptr2 = ptr1; //Copy assignment directly without using Move assert(ptr1 && ptr2); assert(ptr1 == ptr2); //shared_ptr can be compared directly assert(ptr1.use_count() == 2); //Are not unique and the reference count is 2 assert(ptr2.use_count() == 2);
Weak reference pointer
shared_ The reference count of PTR will lead to a new problem, which is circular reference. This is shared_ptr is very easy to appear as a class member. A typical example is the linked list node.
class Node final { public: using this_type = Node; //Type alias using shared_type = std::shared_ptr<this_type>; public: share_ptr next; //Use a smart pointer to point to the next node }; auto n1 = make_shared<Node>(); auto n2 = make_shared<Node>(); assert(n1.use_count() == 1); //Reference count 1 assert(n2.use_count() == 1); //Reference count 1 n1->next = n2; //The two nodes refer to each other, forming a circular reference n2->next = n1; assert(n1.use_count() == 2); //The reference count is 2 assert(n2.use_count() == 2); //It cannot be reduced to 0 and cannot be destroyed, resulting in memory leakage
At this point, we need to use weak_ptr. weak_ptr, as its name implies, has a very "weak" function. It is designed to break the circular reference. It only observes the pointer and does not increase the reference count, but weaks when necessary_ PTR can call the member function lock() to get shared_ptr.
In the above example, we use weak instead_ ptr.
class Node final { public: using this_type = Node; //Type alias using shared_type = std::weak_ptr<this_type>; public: share_ptr next; //Use a smart pointer to point to the next node }; auto n1 = make_shared<Node>(); auto n2 = make_shared<Node>(); assert(n1.use_count() == 1); //Reference count 1 assert(n2.use_count() == 1); //Reference count 1 n1->next = n2; //The two nodes refer to each other, forming a circular reference n2->next = n1; assert(n1.use_count() == 1); //Because of the use of weak_ptr, reference count is 1 assert(n2.use_count() == 1); //Breaking the circular reference will not lead to memory leakage if(!n1->next.expired()) //Check weak_ Is PTR valid { auto ptr = n1->next().lock(); //lock get shared_ptr assert(ptr == n2); }
weak_ The expired() of PTR is equivalent to shared_ Use of PTR_ Count() = = 0, used to judge weak_ Whether PTR is effective.
weak_ Another important function of PTR is to make the class create shared correctly_ ptr. Object using weak internally_ PTR to hold this pointer, then call lock() to get shared_. ptr.
This requires the auxiliary class enable_shared_from_this. Classes that need self-management must use it in the way of inheritance, and then you can use the member function shared_from_this() creates shared_ptr.
Original string
C++11 adds a new expression of "original string" to the literal quantity, with an uppercase letter R and a pair of parentheses more than the original quotation marks, as follows:
auto str = R"(nier:automata)"; //Original string
C + + strings have escape usage: add a "\" in front of them to write "\ n" "\ T" to represent non printable characters such as carriage return, skip, etc. Sometimes we don't want to escape, we just want the original form of the string.
auto str1 = R"(char""'')"; //Output as is: char '' auto str2 = R"(\r\n\t\")"; //Output as is: \ r\n\t\“ auto str3 = R"(\\\$)"; //Output as is:\$
For the form of "quotation marks + parentheses" in the original string, C + + has also prepared a corresponding method, that is, add a special delimiter of up to 16 characters on the left and right sides of the parentheses, so as to ensure that it does not conflict with the content of the string.
For example, the following original string adds "= =" as the delimiter (or any other character sequence):
auto str = R"==(R"(xxx)")=="; //Output as is: R"(xxx)"
String conversion function
New conversion function in C++11
- stoi()/stol()/stoll() can convert strings to integers
- stof()/stod(), etc. can convert strings into floating-point numbers
- to_string() can convert integers and floating-point numbers into strings
assert(stoi("42") == 42); //String to integer assert(stol("253") == 253L); //String to length integer assert(stod("2.0") == 2.0); //String to floating point number assert(to_string(1984) == "1984"); //Integer to string
Literal suffix
C++14 adds a literal suffix "s", which clearly indicates that it is a string type, not a C string. In this way, automatic type derivation can be used when declaring, and in other places where strings are used, the trouble of declaring temporary string variables can be saved, and the efficiency will be higher.
using namespace std::literals; //Namespace must be opened auto str = "std string"s; //The suffix s represents the standard string and automatically deduces the type assert("time"s.size() == 4); //The standard string can call member functions directly
String view
C++17 adds a new string class string_view, which is a string view with low cost. Only one pointer and length are saved internally. It is very cheap to copy or modify.
class string_view { private: const char* m_ptr; std::size_t m_size; public: ... };
There are four key points in this Code:
1. Because constant pointers are used internally, it is a read-only view, which can only view strings and cannot be modified. It is equivalent to "const string &", which is very safe to use.
2. Because the character pointer is used internally, it can be constructed directly from the C string without the temporary object creation operation of "const string &", so it has a wide application area and low cost.
3. Because the pointer is used, you must be careful that the referenced content may become invalid, which is similar to weak_ptr is somewhat similar, both of which are weak references
4. Because it is a read-only view, it cannot be guaranteed that the end of the string must be NULL, and the member function C cannot be provided_ Str (), that is, you can't put string_view is converted into a NULL terminated character pointer, which cannot be used to pass parameters to C functions.
Example:
string_view sv; //Default constructor assert(sv.empty()); //String view is empty sv = "fantasy"s; assert(sv.size() == 7); //Construct from string sv = "c++"; assert(sv.size() == 3); //Construct from C string string_view sv2("std c++", 3); //Specifies the number of characters and the length of the construct assert(sv2 == "std");
We can put string_ As a lightweight "substitute" of string, view is used in the situation of read-only and weak reference, so as to reduce the use cost of string.
When the formal parameter is string_view, you can pass in string, char *, string literal (constant)
If const string & is taken as the parameter, string literal constant and char * cannot be passed in. Only string can be used.
String formatting
C++20 adds a special format library format, which provides a special format function format(), which is similar to printf(), but uses a variable parameter template, which not only supports any number of parameters, but also realizes compile time type checking, which is very safe to use. format() no longer uses the traditional "%", but uses "{}" similar to python/c # with good readability.
format("{}", 100L); //Format output integers directly format("{0}-{0}, {1}, {2}", "hello", 2.718, 3.14); //The following parameters can be referenced with sequence numbers Output: 100 hello-hello, 2.718, 3.14
The basic form of format placeholder is "{serial number: format flag}". In it, you can not only reference the following parameters with serial number (starting from 0), but also add various flags to customize the format. A brief list is as follows:
- <: align data left
- >: align data right
- +: adds a sign to a number
- -: negative numbers are marked with "-" and positive numbers are unmarked
- Space: negative numbers are marked with "-" and positive numbers are preceded by spaces
- b: Format as binary integer
- d: Format as decimal integer
- o: Format as octal integer
- x/X: formatted as hexadecimal integer
- #: non decimal digits display "0b", "0o", "0x" prefixes
- Numbers: width of formatted output
format("{:>10}", "hello"); //Right justified, 10 character width format("{:04}"), {:+04}, 100L, 88); //Specifies the fill and width, which defaults to decimal format("{0:x}, {0:#10} ", 100L); / / formatted as hexadecimal format("{:04o}, {:04b}", 7, 5); //Format as octal / binary, width is 4 Output: hello 0100, +088 64, 0X64 0007, 0101
If you want to output "{}", you need to use a special "{}}" form, for example:
format("{{xxx}}"); //Output {xxx}
regular expression
C++11 introduces regex, a regular expression library. With its powerful function, we can manipulate strings and text arbitrarily.
Regular expression is a special pattern description language, which is specially designed to deal with text. It defines a set of strict syntax rules. According to this set of rules to write patterns, complex matching, search and replacement can be realized.
Basic syntax rules:
- .: matches any single character
- $: end of matching line
- ^: matches the beginning of the line
- (): defines a subexpression that can be referenced or repeated
- *: indicates that the element can be repeated any number of times
- +: indicates that the element can be repeated one or more times
- ?: Indicates that the element can be repeated 0 or 1 times
- |: matches one of the elements on either side of it
- []: define a character set, list a single character, define a range, or a complement of the set
- \: escape character. Special characters match themselves after escape
C + + regular expressions mainly use two classes:
Regex: represents a regular expression, which is basic_ Specialized form of regex
smatch: indicates the matching result of regular expression. It is match_ Specialized form of results
When creating regular expression objects, we can also pass some special flags to control the processing of regular expressions. These flags are located in the namespace STD:: regex_ In constants, the following are commonly used
- icase: case is ignored when matching, that is, case insensitive
- Optimize: it is required to optimize the regular expression as much as possible, but it will increase the construction time of the regular expression object
- ECMAScript: use ECMAScript compatible syntax, which is also the default syntax
- awk/grep/egrep /: use syntax such as awk/grep/egrep.
Ignore case and optimize expression
using namespace std::regex_constants; //Open the namespace where the flag is located regex reg1{"xyz", icase | optimize}; //Ignore case and optimize as much as possible
Regular expression algorithm:
- regex_match: exactly matches a string
- regex_search: find a regular match in the string
- regex_replace: find first and then replace
Therefore, as long as we define an expression object with regex and call the corresponding matching algorithm, we can get the result immediately. However, when writing regular expressions, it is best to remember to use the original string, otherwise the escape character will be tortured.
auto make_regex=[](const auto& txt) //Regular expression creation { return std::regex(txt); }; auto make_match = []() //Return regular matching results { return std::smatch(); };
Regular matching
With the regular expression object, we can call regex_match checks the string, which returns a bool value indicating whether the target exactly matches the pattern defined in the regular expression:
auto reg = make_regex(R"(^(\w+)\:(\w+)$)"); //Use the original string to define a regular expression assert(regex_match("a:b", reg)); //Regular matching succeeded assert(!regex_match("a,b", reg)); //Regular matching failed
regex_match also has an overloaded form containing three parameters. If the match is successful, the result will be stored in the second parameter what, and then access the sub expression like an array:
auto str = "neir:automata"s; auto what = make_match(); //Ready to get matching results assert(regex_match(str, what, reg)); //Regular matching succeeded assert(what[1] == "neir"); //First subexpression assert(what[2] == "automata"); //Second subexpression for(const auto &x : what){ cout << x << ','; //Output neir:automata,neir,automata, }
Use regex_ During match, you should pay attention to that if you want to obtain the capture result, the target string must not be a temporary object, that is, you cannot write the face value directly. If you use the following writing method, a compilation warning will appear:
regex_match("xxx", what, reg); //Cannot be a temporary variable such as literal value
Because the matching result needs to reference the string, and the temporary variable disappears after the function call, which will make the reference invalid.
Regular search
regex_search usage and regex_match is very similar. It does not require full-text matching. Just find a substring that conforms to the pattern. See the following example code:
auto str = "god of war"s; //String to match auto reg = make_regex(R"((\w+)\s(\+w))"); //Use the original string to define a regular expression auto what = make_match(); //Ready to get matching results auto found = regex_search(str, what, reg); //Regular search is similar to matching assert(found); assert(!what.empty()); assert(what[1] == "god"); //First subexpression assert(what[2] == "of"); //Second subexpression
Regular substitution
regex_replace, which is not to match the result, but to provide a replacement string. For example:
auto new_str = regex_replace( //Regular replacement, return new string str, //The original string is unchanged make_regex(R"(\w+$)"), //Regular expression object "peace" //Alternate text needs to be specified ); cout << new_str << endl; //Output god of peace
regex_replace with "^ $" can complete the "pruning" of strings
cout << regex_replace( " xxx ", make_regex("^\\s+"), "") //Remove leading characters << endl; //Output "xxx" cout << regex_replace( " xxx---", make_regex("\\-+$"), "") //Delete the following "-" << endl; //Output "xxx"
Because the algorithm is read-only, regex_replace returns the modified new string. Making good use of this, we can take its output as the input of another function and realize functional programming in the form of "function set function":
cout << regex_replace( //Nested regular substitution regex_replace( str, //The original string is unchanged make_regex("\\w+$"), "peace" ), make_regex("^\\w+"), "godness") //Text to replace << endl; //Output goodness of peace
regex_ The third parameter of replace (replacement text) also follows the PCRE regular expression standard. You can use "$N" to refer to the matching sub expression and "$&" to refer to the whole matching result,
cout << regex_replace( "hello mike", make_regex(R"((\w+)\s(\w+))"), //Regular expression object with two subexpressions "$2-says-$1($&)") //Substitution refers to a subexpression << endl; //Output "Mike says hello (Hello Mike)"
When using regex, we should also pay attention to the cost of regular expressions. Because regular expression objects cannot be statically checked by the C + + compiler, they will only be processed by the regular engine in the running stage, and the cost of syntax checking and dynamic compilation is very high, so we try not to create regular expression objects repeatedly, and reuse them if we can reuse them. We should pay more attention when using circulation. We must put regular expressions outside the circulation.
for(int i = 0; i < 100; ++i) { auto reg = make_regex(R"(\w+)"); //Compile regular expression objects multiple times to reduce operation efficiency }