C + + learning notes (1)
Siddhartha Rao, 21st day learning C + + (8th Edition)
auto
-
Auto auto inference type (new in C++11)
//Initialization is required when using auto auto largeNumber = 25000000000000; //The compiler determines the appropriate value according to the initial value, which should be long long here
-
auto iteration
int a[] = {1,2,3,4,5}; for(auto temp : a) { cout<<temp<<endl; } //Auto & you can modify the value of array a for(auto& temp : a) { temp++; cout<<temp<<endl; }
const; constexpr
-
If the value of a variable should not be changed, it should be declared as a constant.
const double PI = 3.1415926; constexpr double GetPi(){ return 22.0/7;} //Declare constant expressions using constexpr enum CardinalDirections { North, South, East, West }; //Do not use #define to define constants, this method has been abandoned
Do not use #define to define constants, this method has been abandoned.
Dynamic array
std::vector
C + + string
- Avoid C-style strings: use '\ 0' as the terminating null character.
- Use std:string, including #include < string >
Inline function
- When the function is very simple and needs to reduce the overhead, it can be declared as an inline function.
lambda function (new in C++11)
Pointer
-
Pointers are variables that store memory addresses.
int *p = NULL;//Initialization, uninitialized pointers contain values that are garbage (random) and may cause problems (illegal access).
-
Use the reference operator & to get the address of the variable.
-
The dereference operator * accesses the stored value pointing to the address.
-
const modifier pointer
-
The address contained in the modifier pointer is a constant and cannot be modified, but the data pointed to by the pointer can be modified.
int daysInMonth = 30; int* const pdaysMonth = &daysInMonth; *pDaysMonth = 31;//OK! int daysInLUnarMonth = 28; pDaysMonth = &daysInLUnarMonth;//Not OK!
-
The data pointed to by the modified pointer is a constant and cannot be modified, but the address contained in the pointer can be modified.
int daysInMonth = 30; const int* pdaysMonth = &daysInMonth; *pDaysMonth = 31;//Not OK! int daysInLUnarMonth = 28; pDaysMonth = &daysInLUnarMonth;//OK! int* newMonth = pDaysMonth;//Not OK!
-
The address contained in the modifier pointer and the value it points to are constants and cannot be modified.
int daysInMonth = 30; const int* const pdaysMonth = &daysInMonth; *pDaysMonth = 31;//Not OK! int daysInLUnarMonth = 28; pDaysMonth = &daysInLUnarMonth;//Not OK!
-
-
Arrays can be assigned to pointers.
-
Common errors in using pointers:
- Memory leak
- Pointer to useless memory unit: uninitialized
new(std::nothrow)
- New (STD:: nothlow), as the name suggests, does not throw exceptions. When an object fails to new, the object is set to NULL by default. In this way, you can easily judge whether the new operation is successful through if(p == NULL).
- Ordinary new operation will throw an exception if the memory allocation fails. Although if(p == NULL) will be written later, it is actually self deception, because if the allocation is successful, p must not be NULL; If the allocation fails, the program will throw an exception, and the if statement cannot be executed at all.
- Therefore, it is recommended to use new (STD:: nothlow) and then if(p==NULL) to judge any new operation in c + + code
#include <iostream> using namespace std; int main() { int* points = new(nothrow) int[10]; if(points) { cout<<"Memory allocation successed. Ending program"<<endl; delete[] points; } else{ cout<<"Memory allocation failed. Ending program"<<endl; } return 0; }
quote
-
Reference is the alias of the corresponding variable.
int flag = 30; int& temp = flag;
-
const modify reference: prohibit modifying the value of the variable it points to through reference.
-
One of the advantages of passing parameters to functions by reference is that it can avoid assigning formal parameters to formal parameters, which greatly improves performance.
#include <iostream> using namespace std; //If a function receives a very large object, the cost of passing by value will be very large. By using references, the efficiency of function calls can be greatly improved. Don't forget to use const for reference parameters unless the function needs to store the result in the parameter. void GetSquare(const int& number, int& result) { result = number*number; } int main() { int number = 0; cin >> number; int square = 0; GetSquare(number,square); cout<<number<<"^2 = "<<square<<endl; return 0; }
Classes and objects
copy constructor
What is shallow copy? Only copy constructors initialized by ordinary variables are shallow copies. How to calculate ordinary variables? as int,char, string...Pointer variables are not involved. You can use the default copy constructor directly. What is deep copy? Instead of the default copy constructor, you can explicitly define a copy constructor and allocate dynamic memory inside it again, which is called deep copy. Generally speaking, if the class involves pointer variables, you need to apply inside the copy constructor. The most direct way to judge whether to use shallow copy or deep copy is to check whether there are pointer variables in the class.
-
Shallow copy: if the class contains pointer variables, multiple objects will share the same resource, and the same resource will be released multiple times, crash or memory leak.
#include <iostream> using namespace std; // Deep and shallow copy operation class Person { public: // non-parameter constructor Person() { cout << "Person Constructor call for" << endl; } // Parameterized constructor Person(int a, int h) { m_Age = a; m_Height = new int(h); cout << "Person Parameterized constructor call" << endl; } // Analytic constructor ~Person() { // Release the space opened up by the reactor area if(m_Height != NULL) { delete m_Height; m_Height = NULL; // Prevent wild pointer from appearing } cout << "Person Constructor call for" << endl; } // Implement the copy constructor to solve the problems caused by shallow copy //Be sure to declare the parameters of the accepted source object as const references Person(const Person &p) { cout << "Person Copy function call" << endl; m_Age = p.m_Age; // m_Height = p.m_Height; // The default implementation of the compiler is this line of code (shallow copy) // Deep copy operation m_Height = new int(*p.m_Height); } int m_Age; // Age int *m_Height; // height }; void test01() { Person p1(18, 160); cout << "Person Age:" << p1.m_Age << "Height:" << *p1.m_Height << endl; Person p2(p1); // Perform a shallow copy operation (call the default copy function) cout << "P2 Age:" << p1.m_Age << "Height:" << *p2.m_Height << endl; } int main(int argc, char const *argv[]) { /* code */ test01(); return 0; }
Singleton class
28.C + ± single example template (detailed explanation) - Nuo Qian - blog Park (cnblogs.com)
explicit
-
The explicit keyword is used to prevent implicit automatic conversion of class constructors
-
The explicit keyword is only valid for class constructors with one parameter. If the class constructor parameters are greater than or equal to two, there will be no implicit conversion, so the explicit keyword is invalid
-
The explicit keyword only needs to be used in front of the single parameter constructor in the class. Since nonparametric constructors and multiparametric constructors always show calls, it makes no sense to add explicit before the constructor in this case.
-
The advantage of explicit mentioned in google's c + + specification is that it can avoid inappropriate type transformation and has no disadvantages. Therefore, google stipulates that all single parameter constructors must be displayed. In very few cases, the copy constructor can be called explicit without declaration. For example, a class that acts as a transparent wrapper for other classes.
-
effective c + + says that constructors declared explicit are usually more popular than their non explicit brothers. Because they prohibit the compiler from performing unexpected (and often unexpected) type conversions. Unless I have a good reason to allow constructors to be used for implicit type conversion, I will declare it explicit and encourage everyone to follow the same policy.
inheritance
-
is-a: public inheritance
Subclasses and subclass objects can access the public members of the base class.
-
has-a:
-
Protect inheritance
Subclasses can access the public and protect methods of the base class, but subclass objects cannot access the public members of the base class.
-
Private inheritance
Subclasses and objects of subclasses cannot access public members of base classes.
-
-
Use final to prohibit inheritance (C++11)
polymorphism
Using virtual functions to realize polymorphism
-
For base class methods that will be overridden by derived classes, be sure to declare them as virtual functions.
-
For base class destructors, be sure to provide a virtual destructor.
Pure virtual function
- Pure virtual function causes the class to become an abstract base class, and the implementation of virtual function must be provided in the derived class.
- Abstract base class cannot be instantiated.
//Abstract base class (ABC) class AbstractBase { public: virtual void DoSomeThing() = 0; //Pure virtual function }; //The above declaration tells the compiler that the derived class of AbstractBase must implement the method DoSomeThing(); class Derived: public AbstractBase { public: void DoSomeThing() { cout<<"Implemented virtual function"<<endl; } };
Virtual inheritance
-
If a derived class may be used as a base class, it is best to derive it using the keyword virtual:
class Derived1 : public virtual Base { }; class Derived2 : public virtual Base { }; //And the keyword final is used to prohibit the use of SubDerived as the base class class SubDerived final: public Derived1, public Derived2 { };
In the inheritance hierarchy, when inheriting multiple base classes derived from the same class, if these base classes do not adopt virtual inheritance, it will lead to ambiguity. This ambiguity is called the Diamond Problem.
override(C++11)
When declaring a function in a derived class to override a base class function, be sure to use override.
Override provides a powerful way for programmers to explicitly express their intention to override the virtual functions of the base class, and then let the compiler check as follows:
- Is the base class function a virtual function?
- Is the characteristic identifier of the corresponding virtual function in the base class exactly the same as the function declared as override in the derived class?
class Base { virtual void f(); }; class Derived : public Base { void f() override; // Overriding a derived f unction base class void F() override;//Error: function F does not override any virtual functions in the base class };
- overide function: remind yourself to rewrite this function with the same parameter in the derived class. If you don't write it, you will report an error.
operator
- Theoretically, the prefix operator is better than the suffix operator++ Value is better than value + +.
- For classes with original pointers, be sure to implement copy constructors and overloaded assignment operators.
Operator overloading
return_type operator operator_symbol(...parameter list...)
#include<iostream> using namespace std; class Box { double length; //length double width; //width double height; //height public: Box(){} Box(double length,double width, double height):length(length),width(width),height(height){} ~Box(){} void print(void) { cout<<length<<","<<width<<","<<height<<endl; } //Overload + operator to realize Box addition Box operator + (const Box& b) { Box box; box.length = this->length + b.length; box.width = this->width + b.width; box.height = this->height + b.height; return box; } //Overload prefix operator++ Box operator ++ () { ++this->length; ++this->width; ++this->height; return *this; } //Overload suffix operator-- //Note that int is enclosed in parentheses to indicate to the compiler that this is a suffix form, not an integer. Just to distinguish between pre and post Box operator -- (int) //Add int { //Save original value Box temp = *this; //Implicit call to conversion function //Box temp(*this); // Call default copy constructor //Box temp(length,width,height); --this->length; --this->width; --this->height; return temp; //Return original value } /*Here, it is important to declare the operator overloaded function as a friend function of the class, so that we can call the function directly without creating an object. Traditionally, people use CIN > > and cout < < and have to use friend functions to overload operators. If member functions are used to overload operators, D1 < < cout will appear; This unnatural code. */ //It is equivalent to declaring an overloaded operator in the whole global. When the overloaded operator function is a global function, you need to declare the function as a friend in the class //Overload > > friend istream& operator >> (istream& input, Box& b) { input >> b.length >> b.width >> b.height; return input; } //Heavy load<< friend ostream& operator << (ostream& output,const Box& b) { output<<b.length<<","<<b.width<<","<<b.height; return output; } }; int main() { Box box1(5.0,6.0,7.0); Box box2(8.0,9.0,10.0); Box box3 = box1 + box2; box3.print(); //13,15,17 Box ox4 = ++box3; box4.print(); //14,16,18 Box box5 = box3--; box5.print(); //14,16,18 box3.print(); //13,15,17 Box box6; cin >> box6; cout <<box6<<endl; return 0; }
C + + type conversion operator
destination_type result = cast_operator<destination_type> (object_to_cast)
static_cast
- Used to convert between pointers of related types.
Derived objDerived1 = new Derived(); Base* objBase = &objDerived1; //OK! Up conversion without explicit conversion Derived* objDerived2 = objBase;//Error! Down conversion, explicit conversion required //OK! Derived* objDerived2 = static_cast<Derived*>objBase;
//static_cast only verifies whether the pointer type is relevant, and does not perform any run-time checks. Base* objBase = new Base(); Derived* objDer = static_cast<Derived*>objBase;//Not Error! But it may lead to unexpected results.
- Converts the type of the standard data type that was originally implicit to explicit.
dynamic_cast
-
And static_cast, on the contrary, dynamic_cast performs conversion during the run-time phase, and dynamic can be checked_ The result of cast operation to judge whether the type conversion is successful.
Base* objBase = new Derived(); Derived* objDer = dynamic_cast<Derived*>(objBase); //Be sure to check dynamic_ The return value of cast to see if it is valid. If the return value is NULL, the conversion fails. if(objDer) { objDer->CallDerivedFunction(); }
-
This mechanism for identifying object types at runtime is called runtime type identification (RTTI)
reinterpret_cast
- Cast.
- Reinterpret should be avoided as much as possible_ cast.
const_cast
-
Allows programmers to turn off the object's access restriction const.
//DisplayMembers() should have been const, but it is not defined in this way. And the class SomeClass belongs to a third-party library and cannot be modified. void DisplayAllData(const SomClass& object) //Obviously, const should be used here { object.DisplayMembers();//Compilation error //Reason: use const reference to call the member of non const. } void DisplayAllData(const SomClass& object) { SomeClass& refData = const_cast<SomeClass&>(object); object.DisplayMembers();//OK! }
-
Const is unnecessary_ Cast to call non const functions. The result of this is unpredictable!
Except dynamic_ Type conversion outside cast can be avoided; Type conversions should be avoided as much as possible.
macro
Use #define to define constants
- Preprocessing is just a rigid text replacement without checking whether the replacement is correct.
#define PI 3.1415926 / / the preprocessor cannot determine the data type //A better way to define constants is to use const const double PI = 3.1415926; typedef double MY_DOUBLE; //#define MY_DOUBLE double
Use macros to avoid multiple inclusion
-
Preprocessor compilation instruction: #ifndef #endif
#ifndef _HEAD_H_ #define _HRAD_H_ //head. Content of H #endif //end of head.h
Writing macro functions using #define
- Be careful to use parentheses to ensure priority
Validate expressions using assert macro
- Include #include < assert h>.
- assert is used to debug and verify the input parameters.
- assert() is not available in publish mode.
- It is recommended to use assert() extensively in the code. Although it is disabled in the release version, it is very helpful to improve the code quality.
Try not to write macro functions by yourself; Use const constants instead of macro constants. Macros are not type safe.
Template
- Template is undoubtedly one of the most powerful features in C + + language. Templates are type safe.
- In the compiler's view, the code exists only when the template is used.
- For templates, instantiation refers to using one or more template parameters to create a specific type.
Template declaration template
template<typename objType>
template function
#include<iostream> using namespace std; //template function template<typename objType> const objType& GetMax(const objType& value1, const objType& value2) { return value1 > value2 ? value1 : value2; } int main() { int num1 = 25, num2 = 40; //int maxVal = GetMax<int>(num1,num2); // Template functions can not specify the type explicitly, but template classes must be specified explicitly. int maxVal = GetMax(num1,num2); cout<<maxVal<<endl; return 0; }
template class
#include<iostream> using namespace std; //template class template<typename T1, typename T2 = T1> //The default type of T2 is T1 //Template < typename T1 = int, typename T2 = string > / / template declaring default parameters class MyTemplate { T1 member1; T2 member2; public: MyTemplate(const T1& t1, const T2& t2):member1(t1),member2(t2){} const T1 getObj1() const{return member1;} //The return value is const; This pointer is const T2 getObj2(){return member2;} //... }; //use int main() { MyTemplate<int ,double> temp1(10,19.5); MyTemplate<int ,string> temp2(10,"hello world!"); cout<<temp1.getObj1()<<endl; cout<<temp2.getObj2()<<endl; return 0; }
Template with variable number of parameters (C++11)
//Here, the class keyword indicates that T is a type. Later, in order to avoid confusion caused by the use of class in these two places, the keyword typename is introduced. Its function is the same as that of class, indicating that the following symbol is a type //template <class... T> template<typename... T> void f(T... args); sizeof...(args) //sizeof... () print the number of variable parameters
In the definition of variable template parameters above, the ellipsis has two functions:
- Declare a parameter package T... args, which can contain 0 to any template parameters;
- On the right side of the template definition, you can expand the parameter package into an independent parameter.
We cannot directly obtain each parameter in the parameter package args. We can only obtain each parameter in the parameter package by expanding the parameter package.
There are generally two methods to expand variable template parameter functions: one is to expand parameter packages through recursive functions, and the other is to expand parameter packages through comma expressions.
-
Expand parameter package by recursive function
#include <iostream> using namespace std; //To expand the parameter package through recursive function, you need to provide a parameter package expansion function and an overloaded recursive termination function. The recursive termination function is used to terminate recursion. //Recursive termination function template <typename T> void print(T head) { cout << "parameter " << head << endl; } //Expansion function template <typename T, typename ...Args> void print(T head, Args... rest) { cout << "parameter " << head << endl; print(rest...); } int main(void) { print(1,2,3,4); return 0; }
-
Expand parameter package with comma expression
#include <iostream> using namespace std; //This method requires the help of comma expression and initialization list template <typename T> void printarg(T t) { cout << t << endl; } template <typename ...Args> void expand(Args... args) { int arr[] = {(printarg(args), 0)...}; } int main(void) { expand(1,2,3,4); return 0; }
static_assert(C++11)
-
Execute compile time assertion: prohibit unwanted template instantiation.
//Template instantiation against int is prohibited static_assert(sizeof(T) != sizeof(int),"No in please!"); //Compilation prohibited if conditions are not met
Standard template library
STL container
A container is an STL class used to store data.
Sequential container
As the name suggests, sequential containers store data in order, such as arrays and lists. Fast insertion speed, full search.
- std::vector: regular dynamic array.
- std::deque: dynamic array, but it is allowed to insert or delete at the beginning.
- std::list: bidirectional linked list.
- std::forward_list: one-way linked list.
Associated container
Stores data in the specified order. Insertion is slow and search is fast.
- std::set: Set
- std::unordered_set:
- std::map: key value pair.
- std::unordered_map:
- std::multiset: similar to set, do not evaluate unique items. Multiple items with the same value can be stored.
- std::unordered_multiset:
- std::multimap: similar to map, do not require the key to be unique.
- std::unordered_multimap:
Container Adapter
- std::stack: stack
- std::queue: queue
- std::priority_queue:
STL iterator
STL algorithm
STL string class
- std::string: simple string
- std::wstring: wide string
STL string class
- #include
- std::string materializes std::basic_string template class
Instantiate string
#include <string> #include <iostream> using namespace std; void print(const string& str,const string& s) { cout<<s<<": "<<str<<endl; } int main() { string str0; //Generate empty string str string str1 = "Hello String!"; const char* Cstr = "Hello Char*"; string str2(str1,6); //Copy from "index 5" string str3(Cstr,5); //Take the first five characters of C string as the initial value of string s. string str4("Hello String!",5); string str5(10,'a'); //Generates a string containing n c characters string str6(str1,0,5);//The part of the string str that starts with index and has a maximum length of n is taken as the initial value of the string string str7(str5.begin(),str5.begin()+5); print(str0,"str0"); print(str1,"str1"); print(str2,"str2"); print(str3,"str3"); print(str4,"str4"); print(str5,"str5"); print(str6,"str6"); print(str7,"str7"); /*output str0: str1: Hello String! str2: String! str3: Hello str4: Hello str5: aaaaaaaaaa str6: Hello str7: aaaaa */ return 0; }
string addition, deletion, modification and query
#include <string> #include <iostream> #include <algorithm> using namespace std; //Traversal to get elements void display() { //size(),length() / / returns the number of characters string str("hello world!"); //size_t is the alias of unsigned int and unsigned long. The compiler replaces the standard type according to different system types to enhance portability for(size_t i = 0; i < str.length(); ++i) { cout<<"str["<<i<<"]: "<<str[i]<<endl; } cout<<endl; //Reverse iterator mode for(auto t = str.rbegin(); t != str.rend(); ++t) { cout<<*t<<","; } cout<<endl; cout<<str.c_str()<<endl;//Change content to C_string return } //Splice string void append() { //+=,append() //push_ / / add character back () at the end string str1("Hello World!"); string str2("Hello Programer!"); str1 += str2; //+= cout<<" str1 += str2, str1: "<<str1<<endl; str1.append(str2); //append() cout<<" str1.append(str2)2, str1: "<<str1<<endl; str2.push_back('?'); cout<<" str2.push_back('?'), str2: "<<str2<<endl; //push_back() adds a character at the end } //Find character (string) void find() { string s("dog bird chicken bird cat"); cout << s.find("bird") << endl; //Find the substring s and return the found location index, cout << (int)s.find("pig") << endl; //-1 indicates that the substring cannot be found cout << s.rfind("bird") << endl; //Reverse the search of substring s and return the found location index, cout << s.rfind('i') << endl; cout << s.find_first_of("13r98") << endl; //Find the first character belonging to a substring cout << s.find_first_not_of("dog bird 2006") << endl;//Find the first character that does not belong to a string } //delete void erase() { string s("123456789abcd!"); //iterator erase(iterator p);// Delete the character indicated by P in the string s.erase(s.begin()); cout << s << endl; //out: 23456789abcd! //iterator erase(iterator first, iterator last);// Delete all characters on the iterator interval [first,last) in the string s.erase(s.begin()+2,s.begin()+3); cout << s << endl; //out: 2356789abcd! //string& erase(size_t pos = 0, size_t len = npos);// Delete the len characters from the index position POS in the string s.erase(0,2); cout << s << endl; //out: 56789abcd! //void clear();// Delete all characters in the string s.clear(); cout << s << endl; } //compare void compare() { string s1("abcdef"), s2("abc"); //Equal returns 0, greater than positive, less than negative cout << s1.compare("abcdef") << endl; //Equal, print 0 cout << s1.compare(s2) << endl; //S1 > S2, print > 0 cout << s1.compare("abyz") << endl; //S1 < "abyz", print < 0 cout << s1.compare(0,3,s2) << endl; //The first three characters of s1 = = s2, print 0 } //Inversion algorithm //The required header file is #include < algorithm > void reverse() { string str("hello string!"); cout<<"str: "<<str<<endl; reverse(str.begin(),str.end()); cout<<"reverse str: "<<str<<endl; } int main() { //display(); //append(); //erase(); find(); compare(); reverse(); return 0; }
STL vector Class
- Header file #include < vector >
Instantiate vector
#include <vector> using namespace std; int main() { vector<int> intArray; //Default construction vector<float> floatArray{20.4, 15.9}; //Initializes the values of two elements vector<int> intArray2(10); //Initialization contains 10 elements vector<int> intArray3(10, 1);//The initialization contains 10 elements with values of 1 vector<int> intArray4(intArray3); //Initialize with an instance return 0; }
vector addition, deletion, modification and query
#include <vector> #include <iostream> using namespace std; //ergodic void traverse() { vector<int> temp{10,12,14,16}; for(auto t : temp) { cout<<t<<" "; } cout<<endl; cout<<"temp[3]: "<<temp[3]<<endl; //Note that the index cannot be out of bounds cout<<"temp[2]: "<<temp.at(2)<<endl; //at() is safer to perform checks during the run-time phase } void traverse(const vector<int>& temp) { cout<<"size = "<<temp.size()<<": "; for(auto t : temp) { cout<<t<<" "; } cout<<endl; } //Insert, delete void insert() { vector<int> integers; //push_back() adds an element at the end, preferred integers.push_back(1); integers.push_back(2); traverse(integers); //insert() inserts elements at the specified position, which is inefficient integers.insert(integers.begin()+1, 25); //Insert 25 after the first element integers.insert(integers.end(),2,45); //Insert two elements at the end of the array, both with a value of 45 traverse(integers); vector<int> temp = {15,16}; integers.insert(integers.begin(), temp.begin(), temp.end());//Insert the value of temp to the beginning of the array traverse(integers); //pop_back() deletes the element from the end integers.pop_back(); traverse(integers); //Empty element integers.clear(); traverse(integers); //Air judgment if(integers.empty()) { cout<<"The container is now empty!"<<endl; } } int main() { traverse(); insert(); return 0; }
- size(): the number of elements actually stored; capacity(): total capacity. Size of array < = <= < = capacity. (if the array is inserted, the vector needs to reallocate the internal buffer. The logical implementation of reallocation is intelligent: allocate more capacity in advance).
- When reallocating internal buffers, you need to copy the objects contained in the container, which may reduce performance.
STL deque class
- Include #include < deque >
- It has all the properties of vector.
- Using push_front and pop_front inserts / deletes elements at the beginning.
STL list class
- Include #include < list >.
- Two way linked list.
- Iterators can be + + or --.
- Although STL provides two algorithms: sort() and remove(), list also provides these two algorithms. The member function version * * of these algorithms ensures that the iterator pointing to the element remains valid after the relative position of the element changes**
Instantiate list
#include<list> #include<iostream> using namespace std; int main() { list<int> linkInts0; //Initialize empty linked list list<int> linkInts1(10); //Initialize a list of 10 elements list<int> linkInts2(10,99);//Contains 10 elements, all 99 list<int> linkInts3(linkInts2);//Copy structure vector<int> vecInts(10,200); list<int> linkInts4(vecInts.begin(),vecInts.end());//Copy from vector object return 0; }
list addition, deletion, modification and query
#include<list> #include<iostream> using namespace std; //ergodic template<typename T> void display(const T& container) { for(auto element = container.cbegin(); element != container.cend(); ++element) { cout<<*element<<" "; } cout<<endl; } //insert void insert() { list<int> listInts; listInts.push_back(10); listInts.push_back(20); listInts.push_front(0); display(listInts); //iterator insert(iterator pos, const T& x) /* Since it is a linked list, its iterator cannot perform addition and subtraction, but it can increase and decrease itself */ //listInts.insert(listInts.begin()+1, 5);// error: no match for 'operator+' (operand types are 'std::_Fwd_list_iterator<int>' and 'int') listInts.insert(++listInts.begin(), 5); //(insertion position, value) display(listInts); //void insert(iterator pos, sizer_type n, const T& x) listInts.insert(listInts.end(), 2, 0); //(insertion position, number of inserts, value) display(listInts); /*template<class InputIterator> void insert(iterator pos, InputIterator f, InputIterator l); */ list<int> listInts1; listInts1.insert(listInts1.begin(), listInts.begin(), listInts.end()); display(listInts1); } //delete void erase() { list<int> listInts{0,10,20,30,40}; listInts.erase(listInts.begin()); //delete display(listInts); listInts.clear(); //empty } //Reverse, sort bool Sort_Desc(const int& t1, const int& t2) //Binary predicate that tells sort how to interpret less than. { return (t1 > t2); } void sort() { list<int> listInts{-20,20,10,100,40}; //reversal listInts.reverse(); display(listInts); //sort //Default sort listInts.sort(); display(listInts); //Advanced sorting listInts.sort(Sort_Desc); //Descending order display(listInts); } int main() { sort(); return 0; }
- Sort the list containing objects
- In the class of the object contained in the list, implement the operator <;
- Provide a sort binary predicate.
STL forward_list(C++11)
- Unidirectional linked list
- Similar to list
- Insert element cannot use push_back; You can only use push_front.
- You can only use + +, not --, for iterators.
- Compared with list, it takes up slightly less memory, because it only needs to point to the next element rather than the previous element.