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
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值