C++11
C++11 can be understood as a new language and is simpler and more efficient.Memory management and threading framework were introduced.
While maintaining efficiency, better abstraction mechanisms are supported.
Maintain performance while improving code security and productivity.
Previous C++ versions could be considered C++98/03.
Modern C++ versions can be considered C++0x/11/14/17/20.
Both cocos2dx3.16.1 and crossapp2.0 have fully supported C++11.
More concise
nullptr
nullptr is used to resolve the ambiguous relationship between NULL and 0.NULL is usually defined as (void*)0.Ambiguity can arise in the following applications.
Entry
#include <iostream> using namespace std; void f(int) { cout << "void f(int)" << endl; } void f(bool) { cout << "void f(bool)" << endl; } void f(void*) { cout << "void f(void*)" << endl; } int main() { f(0); f(NULL); f(false); f(nullptr); }
Run result:
C++ considers 0 to be int first, so calling f(0) calls f(int);
The case of NULL is more complex, and C++ first considers it a generalized integer.If NULL is defined as normal 0, f(int) is called;
If NULL is defined as 0L, there is a conversion:
long -> int, long -> bool, 0L -> void*,
All three cases are legal, and the compiler will error.
With nullptr, there is no ambiguity with overloaded function calls.
nullptr is neither a generalized integer nor a pointer in the general sense.
The actual type of nullptr is std::nullptr_t, which implicitly converts to all the original pointer types, can be considered a pointer to all types.
Use pointer to null after pointer definition in subsequent C++ code:
int * p = nullptr
The flag pointer points to null.
Return value
auto result = findRecord( /* arguments */); if (result == 0) { ... } auto result = findRecord( /* arguments */); if (result == nullptr) { ... }
When 0 is compared with result, the return value type of findRecord cannot be determined for a while (it may be a generalized integer or a pointer type).With nullptr, it is clear that the return value of findRecord must be a pointer type.
override
semantics
override is a function that helps you check to see if you have inherited a function you want to dummy inherit.
The keyword override indicates that this override relationship, if not established, is prompted to the user in the form of an error.
override is a keyword introduced in C++11 to modify overridden virtual functions.Indicates that this virtual function, with the same name and parameter, in the parent class is as unsatisfactory as in the return class, an error will be reported.
The advantage of this is that the compiler will prompt you if you make a mistake in your override, for example, if you can't get the same name, if you want to refer to the same parameter, if you return any, and you will give the compiler a hint that something you might be careless about.
Code Samples
#include <iostream> using namespace std; class Shape { public: virtual void draw(float a) {} }; class Rect :public Shape { public: // void drawx() override //Function names overridden with errors are different // {} // void draw(int a) override //Function parameter types for error overrides are different // {} // Void draw (int a, double d) override //Different number of function parameters for error overrides // {} // int draw() override //Function return value types for error overrides are different // {} // void draw() const override //Error function decorated differently // {} void draw(float a) override {} }; int main() { return 0; }
final
semantics
The keyword final serves two purposes:
First, it prevents inheritance from classes;
Second, block the override of a virtual function.
Code Samples
class A final { public: }; classB: A { public: };
Compiler error: cannot inherit.
class B { public: virtual void func() override final }; class C: B { public: virtual void func(); //error, B::func is final };
Compiler error: Cannot override B::func is final
Significance
We try not to inherit more than three layers when designing our own classes.
The meaning of final is that it prevents an infinite extension of a class.
=default =delete
default
default description
C++ classes have four special types of member functions:
Default constructor,
Destructor,
Copy constructor,
Copy assignment operator.
Special member functions of these classes are responsible for creating, initializing, destroying, or copying objects of the class.
If a programmer does not explicitly define a special member function for a class and needs to use that special member function, the compiler implicitly generates a default special member function for the class.
Code Samples
#include <iostream> using namespace std; class A { public: A() {} //Default no-null construction ~A() {} //Empty destructions A(const A &) {} //Default Shallow Copy A & operator=(const A &) {} //Default shallow assignment //A() = default; //Indicates that you do not need to implement it yourself, using the default constructor of the system //~A() {} = Default //Indicates that the default destructor of the system is used instead of its own implementation //A(const A&) {}= default //Indicates that the default copy construction (shallow copy) of the system is used instead of its own implementation //A&operator=(const A&) {} = Default //Indicates that the default copy assignment (shallow assignment) of the system is used instead of its own implementation A(int x) :_x(x) { } private: int _x; }; int main() { A a; return 0; }
delete
delete description
To enable programmers to explicitly disable a function, the C++11 standard introduces a new feature: "=delete" function.Programmers only need to'=delete;'after the function declaration.This function can be disabled.
Code demonstration
We demonstrate this by using a single example:
#include <iostream> using namespace std; class Singleton { public: static Singleton* getInstance() { if (_ins == nullptr) _ins = new Singleton; return _ins; } ~Singleton() = delete; //Destructors use delete Singleton(const Singleton & ) = delete; //Copy construction uses delete Singleton& operator=(Singleton&) = delete; //Use delete for copy assignment private: Singleton() {} = default; static Singleton* _ins; }; Singleton* Singleton::_ins = nullptr; int main() { Singleton* ps = Singleton::getInstance(); Singleton ps(*ps); *ps = Singleton::getInstance(); return 0; }
>>right angle brackets
vector<vector<int>> vector_of_int_vectors;
Native String
Native string description
Strings are provided in C/C++. The escape sequence of strings brings a lot of invariance to the output. When native meaning is needed, it is more troublesome to reverse it.
C++ provides R'()', a native string in which there is no escape or need to be.
But note that the () in will lead to an early end.
Code Samples
#include <iostream> using namespace std; string path = "C:\Program Files (x86)\alipay\aliedit\5.1.0.3754"; string path2 = "C:\\Program Files (x86)\\alipay\\aliedit\\5.1.0.3754"; string path3 = R"(C:\Program Files (x86)\alipay\aliedit\5.1.0.3754)"; string path4 = R"(C:\Program "Files" (x86)\\alipay\aliedit\5.1.0.3754)"; int main(int argc, char* argv[]) { cout << path << endl; //Use escape cout << path2 << endl; //Use escape cout << path3 << endl; //Use native string cout << path4 << endl; //Use native string return 0; }
Run result:
Range-Based for Interval Iterator
Normal for
Code demonstration:
#include <iostream> using namespace std; int main() { int arr[] = { 1,2,3,4,5,6,7 }; for (int i = 0;i < sizeof(arr) / sizeof(arr[0]);++i) { cout << arr[i] << endl; } return 0; }
Range for
Code example:
#include <iostream> using namespace std; int main() { int arr[] = { 1,2,3,4,5,6,7 }; for (auto& i : arr) { cout << i << endl; } string str = "china"; for (auto& ch : str) { cout << ch << endl; } return 0; }
Run result:
You can also change the value inside.
Code demonstration:
#include <iostream> using namespace std; int main() { int arr[] = { 1,2,3,4,5,6,7 }; for (auto & i : arr) { i += 5; } for (auto& i : arr) { cout << i << endl; } return 0; }
But only if
for (auto & i : arr)
References must be used so that both array values can be changed and more efficient.
C++ provides for range usage for for.The traversal of STL (vector/list/map) provides great write convenience.
(range for) statements iterate through each element in a given sequence and perform an action on each value in the sequence.
Its grammatical form is:
for (declaration : expression) statement</code>
The expression part is an object and must be a sequence, such as a curly bracketed list of initial values, an array, or an object of type vector s or string s.The common feature of these types is that they have begin and end members that return iterators.
The declaration section defines a variable that will be used to access the underlying elements in the sequence.Each iteration, the variable in the declaration section is initialized to the next element value in the expression section.The easiest way to ensure type compatibility is to use the auto type specifier.
for real-world STL code demonstration
#include <iostream> #include <vector> #include <map> using namespace std; int main() { vector<string> vs = { "abc","xyz","mnq" }; vector<string>::iterator itr = vs.begin(); for (; itr != vs.end(); itr++) { cout << *itr << endl; } for (auto& s : vs) { cout << s << endl; } map<int, string> mis = { {1,"c++"}, {2,"java"}, {3,"python"} }; map<int, string>::iterator map_itr = mis.begin(); for (; map_itr != mis.end(); ++map_itr) { cout << (*map_itr).first << "\t" << map_itr->second << endl; } for (auto & pair : mis) { cout << pair.first << "\t" << pair.second << endl; } return 0; }
Run result:
Initialization list initializar list {}
normal init for general initialization
Several ways to initialize vector/map are introduced, so-called general initialization, which calls the constructor of a class.
For example:
vector<int> vi; vector<int> vi(10,10); vector<int> vi(arr,arr + 10);
Code demonstration:
#include <iostream> #include <list> #include <vector> #include <map> using namespace std; int main() { vector<int> vi; vector<int> vi2(10); vector<int> vi3(10, 1); int arr[10]; vector<int> vi4(arr, arr + 10); list<int> li(10); list<int> li2(10); list<int> li3(10, 1); int arr2[10] = {}; list<int> li4(arr2, arr2 + 10); map<int, string> mis; mis[0] = "first"; mis[1] = "second"; mis[2] = "third"; map<int, string> mis2(mis.begin(), mis.end()); mis.insert(pair<int, string>(3, "fourth")); mis.insert(map<int, string>::value_type(3, "fourth")); for (auto& pair : mis) cout << pair.first << ":" << pair.second << endl; return 0; }
Run result:
Initialization List initialization List {}
By initializing the list, we break the original initialization pattern, making the initialization more intuitive and human-friendly.
Code demonstration:
#include <iostream> #include <list> #include <vector> #include <map> using namespace std; int main(int argc, char* argv[]) { vector<int> vi = { 1,2,3,4,5 }; list<int> li = { 1,2,3,4,5 }; map<int, string> mis = { {1,"c"}, {2,"c++"}, {3,"java"}, {4,"scala"}, {5,"python"} }; mis.insert({ 6,"ruby" }); for (auto & is : mis) { cout << is.first << is.second << endl; } return 0; }
Run result:
Note: Initialization lists and class initialization parameter lists are independent.
initializer_list
** Class constructors can be called with {} instead of () in C++11. **Why?
template< class T > class initializer_list;
A new template type initializer_is provided in C++ 11List.
Initializer_The list object can only be initialized with curly braces {}, and all elements within it are const.
Commonly used for constructors and general function parameters.
Introduce
Used for constructors.
Code demonstration:
#include <iostream> using namespace std; int main() { auto ili = { 1,2,3,4,5 }; initializer_list<int> ili2 = { 1,2,3,4,5 }; for (auto& i : ili) { cout << i << endl; } for (auto & i : ili2) { cout << i << endl; } return 0; }
Run result:
General function parameters
Code demonstration:
#include <iostream> using namespace std; double sum(const initializer_list<double>& il); double average(const initializer_list<double>& ril); int main() { auto il = { 1,2,3,4,5 }; double s= sum({ 1,2,3,4,5 }); cout << "sum = " << s << endl; double avg = average({ 1,2,3,4,5 }); cout << "average = " << avg << endl; return 0; } double sum(const initializer_list<double>& il) { double s = 0; for (auto d : il) s += d; return s; } double average(const initializer_list<double>& ril) { double s = 0; for (auto d : ril) s += d; double avg = s / ril.size(); return avg; }
Constructor parameter (vector vi ={})
Code demonstration:
#include <iostream> #include <list> #include <vector> #include <map> using namespace std; template <typename T> class myarr { private: vector<T> _arr; public: myarr() { cout << "called myarr()" << endl; } myarr(const initializer_list<T>& il) { cout << "calledmyarr(const initializer_list<T>&il)"<<endl; for (auto x : il) _arr.push_back(x); } }; int main() { myarr<int> ma; myarr<int> ma2 = { 1,2,3,4,5 }; return 0; }
Run result:
Uniform Initialization Uniform Initialization Style
#include <iostream> #include <vector> using namespace std; int main() { int i = 3; int ii({3}); int ii{3}; int arr[] = {1,2,3}; int arr2[] ({1,2,3}); int arr2[] {1,2,3}; vector<int> vi = {1,2,3}; vector<int> vi2({1,2,3}); vector<int> vi2{1,2,3}; return 0; }