C + + learning notes

Posted by beginneratphp on Thu, 10 Mar 2022 17:09:08 +0100

C + + learning notes (1)

Siddhartha Rao, 21st day learning C + + (8th Edition)

auto

  1. 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
    
  2. 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

  1. 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

  1. Avoid C-style strings: use '\ 0' as the terminating null character.
  2. Use std:string, including #include < string >

Inline function

  1. 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

  1. 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).
    
  2. Use the reference operator & to get the address of the variable.

  3. The dereference operator * accesses the stored value pointing to the address.

  4. 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!
      
  5. Arrays can be assigned to pointers.

  6. Common errors in using pointers:

    • Memory leak
    • Pointer to useless memory unit: uninitialized

new(std::nothrow)

  1. 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).
  2. 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.
  3. 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

  1. Reference is the alias of the corresponding variable.

    int flag = 30;
    int& temp = flag;
    
  2. const modify reference: prohibit modifying the value of the variable it points to through reference.

  3. 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.

  1. 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

  1. The explicit keyword is used to prevent implicit automatic conversion of class constructors

  2. 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

  3. 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.

  4. 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.

  5. 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

  1. is-a: public inheritance

    Subclasses and subclass objects can access the public members of the base class.

  2. 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.

  3. Use final to prohibit inheritance (C++11)

polymorphism

Using virtual functions to realize polymorphism

  1. For base class methods that will be overridden by derived classes, be sure to declare them as virtual functions.

  2. 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

  1. 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

  1. Theoretically, the prefix operator is better than the suffix operator++ Value is better than value + +.
  2. 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)

Beauty of generalization - wonderful use of C++11 variable template parameters - qicosmos (Jiangnan) - blog Garden (cnblogs.com)

//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:

  1. Declare a parameter package T... args, which can contain 0 to any template parameters;
  2. 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.

  1. 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;
    }
    
    
  2. 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)

  1. 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.

Topics: C++ Programming