C++笔记(2.0)

C++

类和对象

c++面向对象三大特性:封装、继承、多态

1.封装

意义:将属性(成员变量)和行为(成员函数)写在一起,表现事物

访问权限:public;protected;private

struct和class区别:默认访问权限不同

​ struct默认权限public,class默认访问权限private

成员属性设置为私有的优点:

1.可以控制读写权限;

2.对于写,可以检测数据有效性

简单实例

class Student
{
public:
	/*设置姓名*/
	void setname(string name)
	{
		s_name = name;
	}
	/*获得姓名*/
	string getname()
	{
		return s_name;
	}
private:
	string s_name;
};
int main()
{
	Student.s1;
	s1.setname("abc");
	cout<<s1.getname()<<endl;
	
	return 0
}

2.对象的初始化和清理

2.1 构造函数和析构函数

构造函数

​ 1.没有返回值;

​ 2.函数名和类名相同;

​ 3.可以有参数,可以重载

​ 4.创建对象时,构造函数自动调用

析构函数

​ 1.没有返回值;

​ 2.函数名和类名相同;

​ 3.不可以有参数,不可以发生重载

​ 4.对象销毁前,自动调用析构函数

2.2 构造函数分类及调用

1.按参数分类:有参构造和无参(默认)构造函数;
2.按类型分类:普通构造和拷贝构造函数

创建

​ 拷贝构造函数:Person(const Person &p)

调用

括号法显示法隐式转换法
无参Person p1;Person p1;Person p1;
有参Person p2(10);Person p2 = Person(10);Person p2 = 10;
拷贝Person p3(p2);Person p3 = Person(p2);Person p3 = p2;

注:

​ 1.无参构造函数不要加(),会被认为是函数声明

​ 2.Person(10) :匿名对象,当前行执行完成后,系统回收

​ 3.不要利用拷贝构造函数初始化匿名对象 Person(p3) 等价于 Person p3,重定义

​ 4…隐式转换法:Person p4 = 10 等价于 Person p4 = Person(10)

调用时机

​ 1.使用一个已经创建完毕的对象来初始化一个新对象

​ 2.值传递的方式给函数参数传值

​ 3.值方式返回局部对象

调用规则

​ 1.创建一个类,编译器添加默认构造、拷贝构造、析构函数

​ 2.如果写了有参构造函数,编译器不提供默认构造函数;

​ 如果写了拷贝构造函数,编译器不提供其他构造函数.

​ Person(const Person&p)>Person(int age)>Person()

2.3 深拷贝和浅拷贝

1.浅拷贝:编译器提供,简单赋值拷贝工作
带来的问题:堆区内存重复释放
2.深拷贝:自己实现,在堆区重新申请空间,进行拷贝工作

简单实例

class Person
{
public:
	Person()
    {
        cout<<"默认构造函数"<<endl;
    }
    Person(int age,int height)
    {
        m_age = age;
        m_height = new int(height);
        cout<<"有参构造函数"<<endl;
    }
    Person(const Person &p)
    {
        cout<<"拷贝构造函数"<<endl;
        m_age = p.m_age;
        //深拷贝操作
        m_height = new int(*p.m_height);
    }
    ~Person()
    {
        //使用浅拷贝,p2会释放掉堆区内存,导致p1重复释放
        if(m_height != NULL)
        {
            delete m_height;
            m_height = NULL;
        }
        cout<<"析构函数"
    }
    int m_age; //年龄
    int *m_height;//身高
};
int main()
{
    Person p1(18);
    cout<<p1.m_age<<p1.m_height<<endl;
    Person p2(p1);
    cout<<p2.m_age<<p2.m_height<<endl;
        
    return 0;
}

注:如果有堆区开辟,一定要提供深拷贝

2.4 初始化列表

Person(int a,int b,int c):m_A(a),m_B(b),m_C(c)

2.5 类对象作为类成员

构造函数:先调用类成员的构造,在调用自身构造

析构函数:先调用自身析构,在调用类成员的析构(与构造顺序相反)

2.6 静态成员(static)

特点:

​ 1.所有对象共享同一个函数

​ 2.静态成员函数只能访问静态成员变量

class Person
{
public:
    static void func()
    {
        m_A = 100;
        //m_B = 100;//错误,不可以访问非静态成员变量
    }
    static int m_A;
    int m_B;
};
/*静态成员变量,类内声明,类外初始化*/
int Person::m_A = 10;

3.C++对象模型和this指针

3.1 成员变量和成员函数分开存储

1.int m_A;非静态成员变量,属于类的对象上

2.static int m_B;静态成员变量,不属于类的对象上

3.void func();非静态成员函数,不属于类的对象上

4.staticvoid func2();静态成员函数,不属于类的对象上

空类大小:1个字节

3.1 this指针

指向 被调用的成员函数 所属的对象

作用:

​ 1.解决名称冲突

​ 2.在类的非静态成员函数中返回对象本身

class Person
{
public:
    Person& Add(Person p)//Person Add(Person) 拷贝构造函数,会创建一个新的对象
    {
        this->age += p.age;
        return *this;//返回本体
    }
    int age;
};

int main()
{
    Person p1(10);
    Person p2(10);
    p2.Add(p1).add(p1);
    cout<<p2.age<<endl;
    
    return 0;
}

本质:指针常量,不可以修改指针的指向

3.3 常函数和常对象

常函数

成员函数后面加const称为常函数
void func() const等价于const Person *const this

常函数内不可以修改成员属性

加上关键字mutable,在常函数和常对象中可以修改值

常对象

(const Person p)只能调用常函数(void func() const)

4.友元

4.1 全局函数做友元

类里添加函数:friend void func()

可以访问私有内容

class Person
{
	friend void func(Person &p);
public:
    Person()
    {
        p_Name = "asd";
    }
private:
	string p_Name;
}
void func(Person *p)
{
   cout<<p->p_Name<<endl;    
}

int main()
{
    Person p;
    fun(&p);
    return 0;
}
4.2 类做友元

类B里添加 friend class A

类A可以访问类B私有内容

4.3 成员函数做友元

类B里添加friend void A::func()

类A中func()可以访问类B私有内容

5. 运算符重载

5.1 加号+

成员函数

Person operator+(Person &p)  
{
    Person temp;
    temp.m_A = this->m_A+p.m_A;
    temp.m_B = this->m_B+p.m_B
}

Person p3 = p1+p2

全局函数

Person operator+(Person &p1,Person &p2)

5.2 左移<<

只能利用全局函数重载左移运算符

ostream& operator<<(ostream &cout,Person &p) //ostream输出流类型

友元访问私有成员

5.3 自加++

前置:

Person &operator++()
{
	m_Num++;//先运算
    return *this;//再返回
}

返回引用,可以对同一个数多次操作(链式操作)

后置:

Person operator++(int)//int代表占位参数,区分前置和后置
{
	Person temp = *this;//先记录当前结果
    m_Num++;//再递增
    return temp;//最后返回记录结果    
}

返回 值,在函数内使用一个局部变量记录初值并返回,局部变量使用完被释放,如果返回引

用,属于非法操作

Person &operator++(int)//error

5.4 赋值 =

先判断是否有属性在堆区,如果有先释放,然后深拷贝,最后返回自身

class Person
{
public:
    Person(int age)
    {
        m_Age = new int (age);
    }
    //重载赋值运算符
    Person& operator=(Person &p)
    {
        if(m_Age != NULL)
        {
            delete m_Age;
            m_Age = NULL;
        }
        //深拷贝
        m_Age = new int (*p.m_Age);
        //返回自身
        return *this;
    }
    ~Person()
    {
        if(m_Age != NULL)
        {
            delete m_Age;
            m_Age = NULL;
        }
    }
    int *m_Age;
}

5.5 关系 ==

bool operator==(Person &p)

匿名对象:类型() /Person()

6.继承

作用:可以减少重复代码
语法:class 子类:继承方式 父类
子类也称为派生类,父类也称为基类

6.1 继承方式

公共继承、保护继承、私有继承

​ 1.父类中私有内容无法继承

​ 2.公共继承:父类中公共、保护成员到子类中依然是公共(类外可访问)、保护权限

​ 3.保护继承:父类中公共、保护成员到子类中变为保护权限

​ 4.私有继承:父类中公共、保护成员到子类中变为私有权限

父类中所有非静态成员属性都会被子类继承

顺序:先调用父类构造函数,在调用子类构造函数;析构顺序与构造相反

6.2 同名继承

1.子类对象可以直接访问子类中同名成员函数

2.子类对象加作用域可以访问到父类同名成员

3.当子类与父类拥有同名成员函数,子类会隐藏掉父类中同名函数,加作用域可以访问到父类中同名函数

class Base
{
public:    
	Base()
    {
        m_A = 100;
    }
    void func()
    {
        cout<<"Base-func()"<<endl;
    }
    void func(int a)
    {
        cout<<"Base-func(int a)"<<endl;
    }
public:
	int m_A;
};

class Son : public Base
{
public:
    Son()
    {
        m_A = 200;
    }
    void func()
    {
        cout<<"Son-func()"<<endl;
    }
public:
    int m_A;
};

void test()
{
    Son s;
    cout<<s.m_A<<endl;//Son中的m_A
    cout<<s.Base::m_A<<endl;//Bass中的m_A
    
    s.func();
    s.Base::func();
    s.Base::func(1);
}

int main()
{
    test();
    return 0;
}
6.3 访问静态变量

class Son : public Base

Son s;

静态成员对象静态成员函数
通过对象访问s.m_A – s.Base::m_As.func() – s.Base::func()
通过类名访问Son::m_A – Son::Base::m_ASon::func() – Son::Base::func()
6.4 多继承

语法:

​ class 子类:继承方式 父类1,继承方式 父类2。。。

​ class Son : public Person , public Person2…

当出现二义性问题时,解决方法:添加作用域

6.5 菱形继承

A->B,A->C,B->D,C->D,数据重复,资源浪费

利用虚继承,解决菱形继承问题

class A {}//虚基类

class B : virtual public A {}

class C : virtual public A {}

class D : public B,public C {}

继承虚基类(最大类A)时加上关键字virtual
vbptr:虚基类指针 指向虚基类表,只有唯一数据

7 多态

7.1 多态基本概念

静态多态:函数重载,运算符重载,复用函数名 函数地址早绑定

动态多态:派生类和虚函数实现运行时多态 函数地址晚绑定

动态多态满足条件:

​ 1.有继承关系

​ 2.子类重写父类虚函数(重写:函数返回值、声明、参数列表完全相同)

动态多态使用条件:父类的指针或引用,指向子类对象

优点:

​ 1.代码组织结构清晰

​ 2.可读性强

​ 3.利于前期和后期的扩展以及维护

class Animal
{
    public:
    virtual void speak()//+virtual 虚函数
    {
        cout<<"animal: speak()"<<endl;
    }
};

class Cat : public Animal
{
    public:
    void speak()//重写虚函数
    {
        cout<<"cat: speak()"<<endl;
    }
};

void doSpeak(Animal &animal)
{
    animal.speak();
}

void test()
{
    Cat cat;
    doSpeak(cat);
}

int main()
{
    test();
    return 0;
}

原理:父类中虚函数会创建vfptr(虚函数指针),将函数入口地址存入虚函数表中,子类继承父类,当子类重写虚函数时,会把继承来的虚函数替换掉,替换为自身的函数。当用父类引用指向子类对象时,会从子类虚函数表中找子类函数入口地址。

7.2 纯虚函数和抽象类

纯虚函数:virtual 返回值类型 函数名 (参数列表) = 0

当类中有了纯虚函数,这个类被称为抽象类

特点:

​ 无法实例化对象;

​ 子类必须重写纯虚函数,否则也属于抽象类

class Base
{
public:
    virtual void func1() = 0;//纯虚函数,无法实例化对象
    virtual void func2() = 0;
    void allfunc()
    {
        func1();
        func2();
    }
};

class Son :public Base
{
public:
    //必须重写纯虚函数,否则无法实例化对象
    virtual void func1()
    {
        cout<<"func1()"<<endl;
    }
    void func2()//virtual在派生类中可加可不加
    {
        cout<<"func2()"<<endl;
    }
};

void dofunc(Base *b)//Base *b = new Son
{
    b->allfunc();
    delete b;//释放
}

void test()
{
    dofunc(new Son);
}

int main()
{
    test();
    return 0;
}
7.3 虚析构和纯虚析构

共性:

​ 1.解决父类指针释放子类对象

父类指针在析构时,不会调用子类析构函数,导致如果子类有堆区属性,会导致内存泄漏

​ 2.都需要具体的函数实现

虚析构:可以实例化对象

virtual ~类名(){}

纯虚析构:属于抽象类,无法实例化对象

virtual ~类名() = 0;

类名::~类名(){}

class Base
{
public:
    Base()
    {
        cout<<"base()"<<endl;
    }
    //父类指针在析构时,不会调用子类析构函数,导致堆区数据没有释放干净,内存泄漏
    //~Base()
    //解决方法:虚析构
    virtual ~Base()
    {        
        cout<<"~base()"<<endl;
    }
    //纯虚析构:
    //virtual ~Base() = 0;
    //使用纯虚析构,需要函数的具体实现
    
    
    virtual void speak() = 0;
};
/*
Base::~Base()
{
	cout<<"~base()"<<endl;
}

*/


class Son :public Base
{
public:
    Son(string name)
    {
        cout<<"son()"<<endl;
        m_Name = new string(name);
    }
    ~Son()
    {
        cout<<"~son()"<<endl;
        if(m_Name != NULL)
        {
            delete m_Name;
            m_Name = NULL;
        }
    }
    
    void speak()
    {
        cout<<*m_Name<<"speak()"<<endl;
    }
    
    string *m_Name;
};

void test()
{
    Base *b = new Son("AA");
    b->speak();
    delete b;
}

如果子类中没有堆区数据,可以不写虚析构和纯虚析构

模板

泛型编程

函数模板

template <tempname T>
void swap(T &a ,T &b)
{
	T temp = a;
	a = b;
	b = temp;
}

1.自动类型推到

swap(a,b);

2.显示指定类型

swap<int>(a,b);

类模板

temp <class T>
void func();

func<int>();

普通函数可以发生自动类型转换

函数模板不能发生自动类型转换

调用规则

1.优先调用普通函数

2.可以通过空模板参数列表 强制调用函数模板

3.函数模板可以发生函数重载

4.如果函数模板可以产生更好的匹配,优先调用模板

template<class T1,class T2>
class Person
{
public:
	Person(T1 name,T2 age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
    void show()
    {
        cout<<endl;
    }
private:
    string m_Name;
    int m_Age;
	
}

//常用 指定传入的类型
void print(Person<string ,int>&p)
{
    p.show();
}
void test()
{
    Person<string ,int>p("a",1);
    print(p);
}

继承

template <class T>
class Base
{
    T m;
};

class Son :public Base<int>//必须指定数据类型
{
    Son s;
};

template <class T1,class T2>
class Son1:public Base<T2>
{
   T1 obj;
};

void test()
{
    Son1 <int ,char> child;
}


类外实现

template<class T1,class T2>
class Person
{
public:
	Person(T1 name,T2 age);
    
    void show()
    {
        cout<<endl;
    }
private:
    string m_Name;
    int m_Age;
	
}

template<class T1,class T2>
Person<T1,T2>::Person(T1 name,T2 age)
{
    this->m_Name = name;
    this->m_Age = age;
}

void Person<T1,T2>::show()
{
    cout<<endl;
}

分文件编写

问题:类模板函数创建时机在调用阶段,导致分文件编写时链接不到

解决:

​ 1.直接包含.cpp文件

​ 2.将声明和实现写到同一个文件中,更改后缀名为.hpp

友元

//类外实现  需要提前声明  编译器可以提前识别
template<class T1,class T2>
class Person;

template<class T1,class T2>
void print(Person<T1,T2> p)
{
    
}

template<class T1,class T2>
class Person
{
public:
	Person(T1 name,T2 age);
    
    friend void print<>(Person<T1,T2>);
    
private:
    string m_Name;
    int m_Age;
	
}

STL(标准模板库)

面向对象和泛型编程 ,目的是提高复用性

划分:容器、算法、迭代器

容器和算法之间通过迭代器进行无缝连接。

1.六大组件

1.容器:各种数据结构,存放数据

​ 序列式容器:强调值的排序,每个元素均有固定的位置

​ 关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系

2.算法:各种常用算法

​ 质变算法:运算过程中会改变区间内的元素内容(拷贝、替换)

​ 非质变算法:运算过程中不会改变区间内的元素内容(查找、遍历)

3.迭代器:扮演容器和算法之间胶合剂

种类功能支持运算
输入迭代器对数据只读访问++、==、!=
输出迭代器对数据只写访问++
前向迭代器读写操作,并能向前推进迭代器++、==、!=
双向迭代器读写操作,并能向前和向后操作++、–
随机访问迭代器读写操作,可以以跳跃的方式访问任意数据

常用的迭代器为双向迭代器和随机访问迭代器

4.仿函数:行为类似函数,可作为算法的某种策略

5.适配器:修饰容器或仿函数、迭代器接口的东西

6.空间配置器:负责空间配置与管理

接口:
push() 
pop()


front
back
    

2.vector

//包含头文件
#include <vector>
//标准算法头文件
#include <algorithm>

void test()
{
    //创建一个vector容器,类似数组
    vector<int> v;
    
    //向容器中插入数据
    v.push_back(10);
    v.push_back(10);
    
    //通过迭代器访问容器中的数据
    //起始迭代器,指向容器中第一个元素
    vector<int>::iterator itBegin = v.begin();
	//结束迭代器,指向容器中最后一个元素的下一个位置
    vector<int>::iterator itEnd = v.end();

    //访问
    //第一种遍历
    while (itBegin != itEnd)
    {
        cout<<*itBegin<<endl;
        itBegin++;
    }
    
    //第二种 包含创建
    for(vector<int>::iterator it = v.begin();it!=v.end();it++)
    {
        cout<<*it<<endl;
    }
    
    //第三种 利用STL提供的遍历算法
    for_each(v.begin(),v.end(),print);
}
void print(int val)
{
    cout<<val<<endl;
}


自定义数据类型

class Person
{
public:
	Person(string name,int age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
    string m_Name;
    string m_Age;    
};

void test()
{
    vector<Person>v;
    Person p1("a",10);
    Person p2("b",20);
    
    //向容器中添加数据
    v.push_back(p1);
    v.push_back(p1);
	
    //遍历容器中的数据
    for(vector<Person>::iterator it = v.begin();it!=v.end();it++)
    {
        //(*it) 是一个<Person>数据类型
        //(*it).m_Name 等价于 it->m_Name
        cout<<"name:"<<(*it).m_Name<<"age:"<<(*it).m_Age<<endl;
    }
}

void test2()
{
    vector<Person*>v;
    Person p1("a",10);
    Person p2("b",20);
    
    //向容器中添加数据
    v.push_back(&p1);
    v.push_back(&p1);
	
    //遍历容器中的数据
    for(vector<Person*>::iterator it = v.begin();it!=v.end();it++)
    {
        //(*it) 是一个<Person*>数据类型       
        cout<<"name:"<<(*it)->m_Name<<"age:"<<(*it)->m_Age<<endl;
    }
}

嵌套容器

void test()
{
    vector <vector<int>>v;
    
    //创建小容器
    vector<int>v1;
    vector<int>v2;
    
    //添加数据
    v1.push_back(1);
    v2.push_back(2);

    //将小容器加入大容器中
    v.push_back(v1);
    v.push_back(v2);
    
    //通过大容器,把所有数据遍历一遍
    for (vector<vector<int>>::iterator it = v.begin();it!=v.end();it++)
    {
        //(*it) vector<int>类型
        for (vector<int>::iterator vit = (*it).begin();it!=(*it).end();vit++)
        {
            cout<<*vit<<" ";
        }
        cout<<endl;
    }


}

3.string

本质是一个类

赋值:=、assign

void test()
{
    string str1;
    str1 = "123";			//123

    string str2;
    str2 = str1;			//123
	
    string str3;
    str3 = 'a';				//a
    
    //assign
    string str4;
    str4.assign("a");		//a
    
    //将前3个字符赋值
    string str5;
    str5.assign("hello",3);	//hel
    
    string str6;
    str6.assign(str5);		//hel
    
    string str7;
    str7.assign(5,'a');		//aaaaa

}

字符串拼接:=、append

void test()
{
    string str1 = "aa";
    str1 += "123";			//aa123	
    str1 += '+';			//aa123+  
    
    string str2 = "bb";
    str1 += str2;			//aa123+bb
	
    string str3 = "cc";
    str3.append("dd"); 		//ccdd
    str3.append("123456",3);//ccdd123
    //str3.append(str2);		//ccdd123aa123+bb
    //从第0个位置开始,截取3个字符追加
    str3.append(str2,0,3);	//ccdd123aa1
    
    
    
}

查找和替换 find rfind

void test()
{
    string str1 = "abcdefg";
    int pos = str1.find("df");
    if(pos == -1)
    {
        cout<<"no"<<endl;
    }
    else
    {
        cout<<pos<<endl;		//3  从第0个数开始找  从左往右查询
    }
    
    pos = rfind("de");
    cout<<pos<<endl;			//3  从右向左查询
    
}


void test1()
{
    string str1 = "abcdefg";
    istr1.replace(1,3,"1111");	//从1号位置起,3个字符,替换为1111
    cout<<str1<<endl;			//a1111efg  
}

比较

void test()
{
    string str1 = "abcdefg";
    string str2 = "abcdefg";
	if(str1.compare(str2) == 0)
    {
        cout<<"相等"<<endl;
    }
    //大小不常用,多用于比较是否相等
    if(str1.compare(str2) > 0)
    {
        cout<<"1 > 2"<<endl;
    }
    
    

}

存取

void test()
{
    string str1 = "abcdefg";
    
    //读
    for(int i = 0;i < str1.size();i++)
    {
        cout<<str1[i]<<endl;
        //str.at(i)
    }
    
    //写
    str[0] = 'x';		//xbcdefg
    str.at(1) = 'y';	//ybcdefg	
    
}

插入与删除

void test()
{
    string str = "abcdefg";
 	str.insert(1,"111");
    cout<<str<<endl;		//a111bcdefg
    
    str.erase(1,3);
    cout<<str<<endl;		//abcdefg

}

子串

void test()
{
    string str = "abcdefg";
 	string subStr = str.substr(1,3);
    cout<<subStr;			//bcd
    
    
    //邮箱姓名截取
    string email = "zhang@163.com";
    int pos = email.find("@");		//5
    string usrName = email.substr(0,pos);//zhang
    
}

4.vector (单端数组)

与普通数组的区别:数组是静态空间,vector可以动态扩展

动态扩展:找到更大的内存空间,将数据拷贝新空间,释放原有空间

#include <vector>

void test()
{
    vector<int>v1;//默认构造 无参构造
    for(int i = 0;i<10;i++)
    {
      	v1.push_back(i);
    }
    print(v1);
    
    //通过区间方式进行构造
    vector<int>(v1.begin(),v1.end());
    print(v2);
    
    //n个elem方式构造
    vector<int>v3(10,100);//10个100
    print(v3);
    
    //拷贝构造
    vector<int>v4(v3);
    print(v4);
    
}

void print(vector<int> &v)
{
    for (vector<int>::iterator it = v.begin();it!=v.end();it++)
    {
        cout<<*it<<" ";
    }
}

赋值

void test()
{
    vector<int> v1;
    for(int i = 0;i<10;i++)
    {
      	v1.push_back(i);
    }
    
    //赋值 operator=
    vector<int> v2;
    v2 = v1;
    
    //assign
    vector<int> v3;
    v3.assign(v1.begin(),v1.end());		//[begin,end)

	//n个elem
    vector<int> v4;
    v4.assign(10,100);
    
}

void print(vector<int>&v)
{
    for(vector<int>::iterator it = v.begin();it!=v.end();it++)
    {
        cout<<*it<<" ";
    }
}

注:

//构造
vector<int>v1(10,100);//10个100

//赋值
vector<int> v2;
v4.assign(10,100);

容量和大小

void test()
{
    vector<int> v1;
    for(int i = 0;i<10;i++)
    {
      	v1.push_back(i);
    }
    
    if(v1.empty())		//为真,代表容器为空
    {
        cout<<"empty"<<endl;
    }
    else
    {
        cout<<v1.capacity()<<endl;	//容量
        cout<<v1.size()<<endl;		//大小
    }
    
    //重新指定大小
    v1.resize(15);
    //如果重新指定的比原来长,默认用0填充新的位置
    print(v1);			//0123...8900000  
   
    v1.resize(15,100);    
    print(v1);			//0123... 100 100 100 100 100
    
    //如果重新指定的比原来短,超出的部分被删除
    v1.resize(5);
    print(v1);			//01234
    
}

void print()
{
    for(vector<int>::iterator it = v.begin();it!=v.end();it++)
        cout<<*it<<endl;
}

总结:

​ 判断是否为空:empty

​ 返回元素个数:size

​ 返回容量容器:capacity

​ 重新指定大小:resize

插入和删除

void test()
{
    vector<int> v1;
    v1.push_back(10);
    v1.push_back(10);
    
    //尾删
    v1.push_pop();		//删除最后一个
    
    //插入
    //insert第一个数据是迭代器
    v1.insert(v1.begin(),100);	//100 10 
    
    v1.insert(v1.begin(),2,1000);	//1000 1000 100 10
    
    //删除
    //insert第一个参数是迭代器
    v1.erase(v1.begin());			//1000 100 100 10
    
    v1.erase(v1.begin(),v1.end());	//清空 等价于v1.clear()
    
    
    
    
}

void print(vector<int> &v)
{
    for(vector<int>::iterator it = v.begin();it!=v.end();it++)
    {
        cout<<*it;
    }
}

数据存取

void test()  
{
    vector<int> v1;
    for(int i = 0;i<10;i++)
    {
      	v1.push_back(i);
    }
    
    //获取第一个元素
    cout<<v1.front()<<endl;
    //获取最后一个元素
    cout<<v1.back()<<endl;
}


容器互换

void test()
{
    vector<int> v1;
    for(int i = 0;i<10;i++)
    {
      	v1.push_back(i);
    }
    
    vector<int> v2;
    for(int i = 10;i>0;i--)
    {
      	v1.push_back(i);
    }
    
    //交换
    v1.swap(v2);
    
    
    
   
    vector<int> v;
    for(int i = 0;i<1000;i++)
    {
      	v.push_back(i);
    }
    v.resize(3);
    //容量1000+,大小3  资源浪费
    
    //vector<int>(v) 匿名对象,当前行执行完系统回收
    //按照v当前所用的个数来初始化匿名对象容量
    //v与一个容量3的匿名对象互换
    vector<int> (v).swap(v);
}


void print(vector<int> &v)
{
    for(vector<int>::iterator it = v.begin();it != v.end();it++)
    {
        cout<<*it;
    }
}

用途:swap可以收缩内存空间

预留空间 reserve

void test()
{
    vector<int> v;
    int *p = NULL;
    int num = 0;
    for(int t = 0;i<1000;i++)
    {
        v.push_back(i);
        //判断p是否指向首地址,即开辟次数
        if(p!= &v[0])
        {
            p = &v[0];
            num++;
        }
    }
    
    //如果提前知道数据量较大
    //利用reserve预留空间
    v.reserve(10000);
    
    
}


5.deque (双端数组)

双端数组,可以对头端进行插入删除操作

deque和vector区别

​ vector对于头部的插入和删除效率低,数据量越大,效率越低

​ deque对于头部插入速度比vector快

​ deque访问速度低于vector

deque通过中控器维护每段缓冲区内容



//const  只读 防止误操作
void print(const deque<int>d)
{
    for(deque<int>::const_iterator it = d.begin();it!=d.end();it++)
    {
        cout<<*it;
    }
}

排序 sort

#include <algorithm>
#include <deque>
void test()
{
    deque<int> d;
    d.push_back(10);
    d.push_back(20);
    d.push_back(30);
    d.push_front(100);//头插
	print(d);			//100 10 20 30 
    
    //sort 默认升序
    sort(d.begin(),d.end());
	print(d);			//10 20 30 100
    
    
    
}

void print(const deque<int>&d)
{
    for(deque<int>::const_iterator it =d.begin();it!=d.end();it++)
        cout<<*it;
}

6.stack(栈)

先进后出

不允许有遍历行为:只有栈顶元素可以被外界访问到

void test()
{
    stack<int>s;
    s.push(10);
    
    while(!s.empty())
    {
        //top()查看栈顶元素
        cout<<"栈顶:"<<s.top();
        
        //pop()出栈
        s.pop();
    } 
    s.size();	//0  栈的大小
}

7.queue (队列)

先进先出

入队:push

出队:pop

第一个元素:队头front

最后一个元素:队尾back

不允许有遍历行为:只有队头和队尾可以被外界访问到

class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Score = age;
	}
	string m_Name;
	int m_Age;
};

void test()
{
    queue<Person>q;
    
    Person p1("A",1);
    Person p2("B",2);
    Person p3("C",3);
    
    q.push(p1);
    q.push(p2);
    q.push(p3);
    
    //判断队列不为空,查看队头队尾
    while(!q.empty())
    {
        cout<<"头:"<<q.front().m_Name<<q.front().m_Age;
        
        cout<<"尾:"<<q.back().m_Name<<q.back().m_Age;
        
        //出队
        q.pop();
    }
    q.size();//0
}

8.*list (链表)

数据域:存储的数据

指针域:下一节点地址

链表存储方式不是连续内存空间,因此list迭代器只支持前移和后移,是双向迭代器

优点:动态存储分配,不会造成内存浪费和溢出;可以对任意位置进行快速插入或删除元素

缺点:对于容器遍历速度没有数组快;占用空间比数组大

void()
{
    list<int> L;
    L.push_back(10);
    L.push_back(10);
    
    list<int> L2(L.begin(),L.end());
    
       
}

void print(const list<int> &L)
{
    for(list<int>::const_iterator it = L.begin();it!=L.end();it++)
    {
        cout<<*it;
    }
}

赋值和交换

void test()
{
    list<int>L1;
    L1.push_back(10);
    L1.push_back(20);
    L1.push_back(30);
    L1.push_back(40);
    
    list<int>L2;
    L2=L1;
    
    //交换
    L1.swap(L2);
    
    
    //删除所有的 10
    L.remove(10);
}

list不支持[]和at()访问

list本质是链表,不是连续线型空间存储数据,迭代器不支持随机访问

list<int>::iterator it = L.begin()

it++ 支持单向

it-- 支持双向

it=it+1; 不支持随机访问

翻转和排序

void test()
{
    list<int>L1;
    L1.push_back(10);
    L1.push_back(20);
    L1.push_back(30);
    L1.push_back(40);
    
    //翻转
    L1.reverse();
    
    //排序
    //不支持随机访问迭代器的容器,内部提供对应算法
    
    L1.sort();
}

9.set(二叉树)

关联式容器

所有元素插入时自动排序

#include<set>

void test()
{
    set<int>s1;
    //插入数据只有insert
    s1.insert(10);
    s2.insert(20);
    
}

特点:自动排序;不允许插入重复的值

10.map

map中所有元素都是pair

pair中第一个元素是key(索引),第二个元素是value(实值)

所有元素都会安照key排序

#include <map>
void test()
{
    map<int ,int>m;
    m.insert(pair<int,int>(1,10));
    m.insert(pair<int,int>(2,20));
    
}

void print(map<int,int> &m)
{
    for(map<int,int>::iterator it = m.begin();it!=m.end();it++)
    {
        cout<<(*it).first<<it->second;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
学习并掌握C++2.0(11+14+17+20)的新特性,学习线程及线程池的应用 ---------------------------------------------------给小白学员的3年学习路径及计划技术方面分三块:1.纯开发技术方向2.音视频流媒体专业方向3.项目实战---------------------------------------------------1.纯开发技术方向(1) C++必须要过硬(至少学会10本经典好书)(2) 系统级编程(Windows、Linux),必须特别熟练系统API,灵活运用(3) 框架与工具(Qt、MFC):必须精通其中一种。(4) 架构与设计模式:需要提升一个高度,不再是简单的编码,而是思维模式。(5) 驱动级别(如果有兴趣,可以深入到驱动级:包括Windows、Linux)(6) 最好学习点Java+Html+javascript等WEB技术。2.音视频流媒体专业方向(1) 音视频流媒体基础理论:   必须认真学会,否则看代码就是看天书(2) 编解码方向:精通h.264,h.265(hevc), 包括理论和各个开源库(ffmpeg,libx264,libx265,...)。(3) 直播方向:  精通各种直播协议(rtsp,rtmp,hls,http-flv,...), 钻研各个开源库(live555,darwin,srs,zlmediakit,crtmpserver,...)(4) 视频监控:  理论+开源库(onvif+281818)(EasyMonitor、iSpy、ZoneMinder(web)、...) 3.项目实战(1) Qt项目:  至少要亲手练习10个实战项目(网络服务器、多线程、数据库、图像处理、多人聊天、等等)(2)音视频项目:包括编解码、视频监控、直播等各个方向,都需要亲手实战项目,包括视频服务器、后台管理系统、前端播放器(多端)---------------------------------------------------  第1章 C++11新特性 41). nullptr关键字与新语法 42). auto和decltype类型推导 6 auto讲解 6 auto示例 7 decltype 83). for区间迭代 94). 初始化列表 105). 模板增强 11外部模板 11类型别名模板 12默认模板参数 126). 构造函数 13委托构造 13继承构造 147). Lambda 表达式 158). 新增容器 20std::array 20std::forward_list 21无序容器 22元组 std::tuple 239). 正则表达式 2610). 语言级线程支持 28多线程库简介 2811). 右值引用和move语义 31右值引用和move语义 32转移左值 3412). constexpr 35第2章 C++14新特性 36Lambda 函数 36类型推导 37返回值类型推导(Return type deduction) 37泛型lambda 39[[弃用的]]  [[deprecated]]属性 40二进制数字和数字分隔符 41第3章 C++17新特性 42安装GCC10.2 42安装msys2-x86_64-20200720 42更新镜像 42更新软件库 43安装 MinGW64 等必要的软件 43环境变量Path 43编译命令 43constexpr 44typename 45折叠表达式 47结构化绑定 48条件分支语句初始化 49聚合初始化 50嵌套命名空间 52lambda表达式捕获*this的值 53改写/继承构造函数 54用auto作为非类型模板参数 55__has_include 56fallthrough 57nodiscard 57maybe_unused 58第4章 C++20新特性 59编译命令 59concept 59typename 60explicit 61constinit 62位域变量的默认成员初始化 62指定初始化 63基于范围的for循环初始化 64放宽基于范围的for循环,新增自定义范围方法 65嵌套内联命名空间 66允许用圆括弧的值进行聚合初始化 67unicode字符串字面量 68允许转换成未知边界的数组 68likely和unlikely 69第5章 C++2.0(11/14/17/20)总结与分析 705.1 C语言与C++ 715.2 语言可用性的强化 725.2.1 常量 725.2.2 变量及其初始化 735.2.3 类型推导 745.2.4 控制流 765.2.5 模板 775.2.6 面向对象 815.3 语言运行期的强化 835.3.1 Lambda 表达式 835.3.2 右值引用 865.4 容器 885.4.1 线性容器 885.4.2 无序容器 895.4.3 元组 895.5 智能指针与内存管理 905.5.1 RAII 与引用计数 905.5.2 std::shared_ptr 905.5.3 std::unique_ptr 915.5.4 std::weak_ptr 91第6章 C++2.0多线程原理与实战 93什么是并发 93并发的方式 93为什么使用并发 95线程简介 96创建线程的三种方式 971. 通过函数 972.通过类对象创建线程 993.通过lambda表达式创建线程 101thread线程的使用 101互斥量与临界区 105期物Future 111条件变量 112原子操作 114内存模型 118第7章 C++2.0线程池原理与实战 120线程与线程池的基本原理 1201)、线程 1202)、线程的生命周期 1213)、什么是单线程和多线程 1214)、线程池 1225)、四种常见的线程池 123线程池的架构与流程 123线程池代码实战 125    

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值