C++笔记-代码实现

目录

内存分区

cpp将内存划分为4个区域

  1. 代码区(程序执行前):存放函数二进制代码
  2. 全局区(程序执行前):存放全局变量、静态变量以及常量
  3. 栈区:由编译器自动分配释放,存放函数参数值,局部变量
  4. 堆区:由人分配和释放(new,delete),程序结束之后系统会回收
#include<iostream>
using namespace std;

// 全局变量
int g_a = 0;
int g_b = 1;

// 全局常量
const int c_g_a = 0;


int main()
{
    // 内存分为四个区
    // 代码区,全局区,栈区,堆区

    cout<<"全局变量g_a的地址:"<<&g_a<<endl;
    cout<<"全局变量g_b的地址:"<<&g_b<<endl;

    // 局部变量
    int a = 0;
    int b = 1;
    cout<<"局部变量a的地址:"<<&a<<endl;
    cout<<"局部变量b的地址:"<<&b<<endl;

    // 静态变量
    static int s_a = 0;
    static int s_b = 1;
    cout<<"静态变量s_a的地址:"<<&s_a<<endl;
    cout<<"静态变量s_b的地址:"<<&s_b<<endl;

    //常量
    cout<<"字符串常量 hello world 的地址:"<<&("hello world")<<endl;
    
    cout<<"全局常量c_g_a的地址:"<<&(c_g_a)<<endl;

    // 局部常量
    const int c_l_a = 0;
    cout<<"局部常量c_l_a的地址:"<<&(c_l_a)<<endl;
    
    return 0;
}


全局变量g_a的地址:0x407030
全局变量g_b的地址:0x403010
局部变量a的地址:0x61fe1c
局部变量b的地址:0x61fe18
静态变量s_a的地址:0x407038
静态变量s_b的地址:0x403014
字符串常量 hello world 的地址:0x4040d7
全局常量c_g_a的地址:0x404004
局部常量c_l_a的地址:0x61fe14

观察上述的地址

栈区

不要返回局部变量的地址

#include<iostream>
using namespace std;

int* fun()
{
    int a = 10;
    return &a;
}

int main()
{
    int* p = fun();

    cout<<*p<<endl;//第一次可以打印正确的数字 10 ,因为编译器做了保留
    cout<<*p<<endl;//第二次这个数据不再保留

    return 0;

}

// warning: address of local variable 'a' returned (编译报错)
堆区

new / delete

int* fun()
{
    int* a = new int(10);//在堆区开辟一个内存,值是10,然后将其的地址赋值给指针p
    return a;
}

int main()
{
    int* p = fun();

    cout<<*p<<endl;//10
    cout<<*p<<endl;//10
    cout<<*p<<endl;//10
    
    
    // 手动释放
    delete p;
    
    
    
    
        //创建一个堆区的数组
    int* arr = new int[10];
    for(int i=0;i<10;i++)
    {
        arr[i] = i;
    }

    for (int i = 0; i < 10; i++)
    {
        cout<<arr[i]<<endl;
    }

    // 手动释放
    delete[] arr;
    

    return 0;

}

引用&

起别名
int a = 10;
int& b = a;
cout<<"b = "<<b<<endl;//10

b = 20;
cout<<"a = "<<a<<endl;//20
  • 引用必须初始化
  • 引用初始化之后不能改变
引用做函数参数
#include<iostream>
using namespace std;


// 值传递
void swap01(int a, int b)
{
    int temp = a;
    a = b;
    b = temp;
}


// 地址传递
void swap02(int* a,int* b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}

//引用传递,参数理解为起的别名
void swap03(int& a,int& b)
{
    int temp = a;
    a = b;
    b = a;
}


int main()
{
    int a = 10;
    int b = 99;

    swap01(a,b);//并没有交换两个值
    swap01(&a,&b);//成功交换
    swap03(a,b);//成功交换

    return 0;
}
引用做函数返回值
  • 函数不能返回局部变量的引用
//不能返回局部变量的引用
int& fun01()
{
    int a = 0;
    return a;
}


int main()
{

    int& ref = fun01();
    cout<<ref<<endl;//运行错误


    return 0;
}
  • 函数的调用可以作为左值
// 函数的调用可以作为左值
int& fun02()
{
    static int a = 0;//静态变量,存放在全局区,在程序结束后释放
    return a;
}

int main()
{

    int& ref2 = fun02();
    cout<<"ref2 = "<<ref2<<endl;//0
    cout<<"ref2 = "<<ref2<<endl;//0

    // 函数的调用可以作为左值
    fun02() = 1;
    cout<<"ref2 = "<<ref2<<endl;//1
    cout<<"ref2 = "<<ref2<<endl;//1
    

    
    return 0;
}
引用的本质

引用的本质就是一个指针常量

int main()
{
    int a = 10;
    
    //自动转换为 int* const ref = &a; 指针常量是指向一个不可更改的常量,也说明了引用为什么不可更改
    int& ref = a;
    ref = 20;//内部发现是一个引用,自动帮我们转换为 *ref = 20;
    
    cout<<"a = "<<a<<endl;
    cout<<"ref = "<<ref<<endl;
    
    return 0;
}
常量引用

常量引用主要用来修饰形参,防止误操作(防止实参被修改)

//常量引用,用来修饰形参,防止实参被修改

// 这个函数会将外部的实参修改掉
void printValue01(int& a)
{
    a = 0;
    cout<<"value + 10 = "<<a + 10<<endl;
}

// 这个函数保证了在函数内部外部的实参不被修改,如果想修改,编译不通过
void printValue02(const int& a)
{
    // a = 0; 编译不通过
    cout<<"value + 10 = "<<a + 10<<endl;
}


int main()
{
    int b = 99;
    printValue01(a);//会将a修改为0
    
    printValue02(a);//a不会被修改
    
    return 0;
}

函数重载需要注意的

引用作为函数参数

#include<iostream>
using namespace std;


// 函数重载需要注意的事项
void fun(int& a)// int& a = 10 不合法
{
    cout<<"fun(int& a)"<<endl;
}

void fun(const int& b)// const int& a = 10 合法
{
    cout<<"fun(const int& b)"<<endl;
}



int main()
{

    int a = 10;
    fun(a);//fun(int& a)
    fun(10);//fun(const int& b)


    return 0;

}

函数重载遇到默认参数

// 函数重载遇到默认参数
void fun01(int a,int b = 10)
{
    cout<<"fun01(int a,int b = 10)"<<endl;
}
void fun01(int a)
{
    cout<<"fun01(int a)"<<endl;
}

int main()
{
    fun01(0)//编译不通过
}

类和对象

#include<string>

class Student
{
public:
    string name;
    int stuId;

    void printInfo()
    {
        cout<<"name = "<<name<<", stuId = "<<stuId<<endl;
    }

protected:
    string address;
    
private:
    string password;
    
public:
    void set_address_password(string address,string password)
    {
        address = address;
        password = password;
    }

    void get_address_password()
    {
        cout<<"address "<<address<<", password"<<password<<endl;
    }

};

  • public:类内可以访问,类外也可以访问
  • protected:类内可以访问,类外不可以访问,可以访问继承的父类
  • private:类内可以访问,类外不可以访问,不可以访问继承的父类

struct和class

cpp中,struct和class的唯一区别就是访问权限不同

  • class 默认权限是 私有private
  • struct 默认权限是 公共public

构造函数和析构函数

构造函数和析构函数都是必须有的实现,如果不提供,编译器会提供至少3个函数:默认构造函数(空实现)、析构函数(空实现)、拷贝构造函数 (值拷贝)

如果用户定义了有参构造函数,cpp不会提供无参构造函数,但是会提供拷贝构造函数

如果用户定义了拷贝构造函数,cpp不会提供其它构造函数

  1. 构造函数

    可以重载

    创建对象时调用

  2. 析构函数

    不可以重载

    程序的生命周期终结时调用

#include<iostream>
using namespace std;


class Person
{
public:
    Person()
    {
        cout<<"Person 构造函数"<<endl;
    }
    ~Person()
    {
        cout<<"Person 析构函数"<<endl;
    }
};


void test01()
{
    Person p;
}


int main()
{

    test01();//该行执行完,构造和析构都会执行(理解对象的生命周期)

    Person p;//该行执行完,构造会执行
    cin.get();


    return 0;
}


Person 构造函数
Person 析构函数
Person 构造函数
1
Person 析构函数
构造函数

3种构造的方法

class Person
{
private:
    int age;

public:
    Person()
    {
        cout<<"Person 无参函数"<<endl;
    }
    Person(int age)
    {
        this->age = age;
        cout<<"Person 有参构造"<<endl;
    }
    Person(const Person& p)
    {
        // 将传入的p的所有属性 拷贝到自身
        this->age = p.age;
        cout<<"Person 拷贝构造函数"<<endl;
    }



    ~Person()
    {
        cout<<"Person 析构函数"<<endl;
    }
};


void test01()
{
    // 1 
    Person p;//默认构造
    Person p1(10);//有参构造
    Person p2(p1);//拷贝构造

    // 默认构造函数 不要加()
    //Person p4();//这种写法并不会创建一个对象,编译器会认为这是一个函数声明

    //2 显示法
    Person p5;
    Person p6 = Person(20);
    Person p7 = Person(p5);

    // 匿名对象
    // Person(30);//当前执行结束后,系统会立即回收
    
    //注意: 不要用拷贝构造函数 来创建匿名对象
    //Person(p3);


    // 3 隐式转换
    Person p8 = 10;
    Person p9 = p8;

}
Person fun()
{
    Person p1;
    return p1;//这里返回的并不是p1对象,而是执行力拷贝构造函数,创建了一个新的对象
}

深拷贝和浅拷贝

浅拷贝:简单的赋值拷贝操作

深拷贝:在堆区重新申请空间,进行拷贝操作

如果有属性是在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题

class Car
{
private:
    int car_id;
    double* car_value;

public:
    Car()
    {
        cout<<"无参构造函数";
    }
    Car(int id,double value)
    {
        car_id = id;
        car_value = new double(value);//在堆区new一个空间,返回一个指针
        cout<<"有参构造函数";
    }

    //自己实现拷贝构造函数,解决编译器提供的拷贝构造函数带来的浅拷贝问题
    Car(const Car& car)
    {
        car_id = car.car_id;
        //car_value = car.car_value;//编译器提供的拷贝构造函数,就是这行代码,浅拷贝
        // 深拷贝,解决car_value内存释放冲突问题
        car_value = new double(*car.car_value);
    }

    ~Car()
    {
        if (car_value != NULL)//car_value是在堆区手动创建的,需要手动释放
        {
            delete car_value;
            car_value = NULL;
        }
        cout<<"析构函数"<<endl;
    }
};

初始化列表

class A
{
private:
    int A_a;
    int A_b;
    int A_c;

public:
    A(int a,int b,int c):A_a(a),A_b(b),A_c(c){}
    
};


int main()
{
    A aa(1,3,4);
    
    return 0;
}

A类对象作为B类的属性

class Phone
{
    string name;
}

class Person
{
    int id;
    Phone phone;
}
  • 构造时先构建A的对象,再构造B对象自身
  • 析构函数相反

静态成员

  1. 所有对象都共享同一份数据或函数
  2. 编译阶段就分配内存
  3. 类内声明,类外初始化
  4. 静态成员函数只能访问静态的成员变量
class A
{
 private:
    static string name;//类外访问不到
 public:
    static int id;
    
    static void fun()
    {
        cout<<id<<endl;//只能访问静态成员变量
        cout<<"静态成员函数fun";
    }
};

string A::name = "zhangsan";
int A::id = 0;

int main()
{
    Person p1 = Person();
    
    //访问方式1
    p1.name;
    
    //访问方式2
    Person::name;
       
    return 0;
}

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

空对象占用的内存空间为 1

编译器会给每个空对象分配 1 个字节空间,为了区分空对象占内存的地址,每个空对象也应该有一个独一无二的内存地址

class A
{
    
};

int main()
{
    A a;//空对象 sizeof(a) = 1
}



class B
{
    int aa;//属于类的对象
    static int bb;//不属于类的对象
    void fun();//不属于类的对象
};

int main()
{
    B b;//sizeof(b) = 4
}

this指针

  • 解决命名冲突
  • 返回对象本身 *this
class Person
{
    int age;
    Person(int age)
    {
        this->age = age;//this指针解决命名冲突
    }
    
    Person& addAge01(Person& p)//返回 引用
    {
        this->age += p.age;
        return *this;//返回对象本体
    }
    
    Person addAge02(Person& p)//返回 值
    {
        this->age += p.age;
        return *this;//返回一个新的对象(调用拷贝构造函数)
    }
    
};

空指针访问成员

class Phone
{
    int age;
    void showClass()
    {
        cout<"this is class Phone";
    }
    void showAge()
    {
        if (this == NULL)
        {
            return;
        }
        cout<<"age is "<<age<<;//此处的age其实是this->age
    }
}

int main()
{
    Phone* p = NULL;
    phone.showClass();//正常执行
    phone.showAge();//报错 空指针异常
    return 0;
}

const修饰成员函数

常函数

  • 常函数不可以修改成员属性
  • 成员属性声明时加 mutable,在常函数中就可以修改
class A
{
    //this指针本质是一个指针常量,指针的指向是不可修改的
    //后面加const,意味着指针指向的值也不可以修改
    void showA() const
    {
        //this->age = 10;//会报错,this指针不可以修改
        this->id = 1;//正常执行      
    }
    int age;
    mutable id;//特殊变量,在常函数和常对象中可以修改
}

常对象

  • 声明对象前加 const

  • 常对象只能调用常函数

void fun()
{
    const Person p;//在对象前加const,变为常对象
    //p.age = 10;//错误,不能被修改
    p.id = 1;//正确,可以修改
    
    p.showA();//正确,可以执行 
}

友元

全局函数作友元,可以访问类的私有成员

 #include<iostream>
 #include<string>
 using namespace std;


class Home
{
    //声明全局函数friend_fun是一个友元,就可以访问Home中的的私有成员
    friend void friend_fun(Home* home);

public:

    Home()
    {
        this->sitting_room = "客厅";
        this->bad_room = "卧室";
    }

    string sitting_room;


private:
    string bad_room;
};

void friend_fun(Home* home)
{
    cout<<"公共属性 "<<home->sitting_room<<endl;
    cout<<"私有属性 "<<home->bad_room<<endl;
}



 int main()
 {
    Home h1;
    friend_fun(&h1);
    return 0;
 }

作友元

class TV
{
public:
    Home* home;
    
    TV()
    {
        this->home = new Home;
    }
}

class Home
{
	//类作友元,
	friend class TV;

public:
    Home()
    {
        this->sitting_room = "客厅";
        this->bad_room = "卧室";
    }
    string sitting_room;

private:
    string bad_room;
};

成员函数作友元

class TV
{
private:
        Home* home;

public:   
    void visit01()
    {
        cout<<home->sitting_room<<endl;
        cout<<home->bad_room<<endl;
    }
    
    void visit02()
    {
        cout<<home->sitting_room<<endl;
        //cout<<home->bad_room<<endl;//不可访问
    }
}

class Home
{
    //声明另一个类的成员函数作为友元
    friend void TV::visit01();

public:
    Home()
    {
        this->sitting_room = "客厅";
        this->bad_room = "卧室";
    }
    string sitting_room;

private:
    string bad_room;
};

运算符重载

+运算 符重载,实现两个自定义对象的相加
  • 成员函数重载
  • 全局函数重载
#include<iostream>
using namespace std;

class Person
{
public:
    Person(){}

    Person(int a,int b)
    {
        this->m_a = a;
        this->m_b = b;
    }

    // 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;
        return temp;
    }

    int m_a; 
    int m_b;
};



// 2 全局函数重载 + 运算符
Person operator+(Person& p1,Person& p2)
{
    Person p3;
    p3.m_a = p1.m_a + p2.m_a;
    p3.m_b = p1.m_b + p2.m_b;
    
    return p3;
}

//函数重载的版本
Person operator+(Person& p,int num)
{
    Person p3;
    p3.m_a = p.m_a + num;
    p3.m_b = p.m_b + num;
    
    return p3;
}



int main()
{
//实现两个自定义数据类型相加的运算
    Person p1 = Person(2,3);
    Person p2 = Person(10,20);

    Person p3 = p1 + p2;
    
    Person p4 = p1 + 99;

    cout<<"p3.m_a = "<<p3.m_a<<endl;//12
    cout<<"p3.m_b = "<<p3.m_b<<endl;//23
    
    cout<<"p4.m_a = "<<p4.m_a<<endl;//101
    cout<<"p4.m_b = "<<p4.m_b<<endl;//102
    return 0;
}
<<运算符重载
实现输出一个自定义对象的各属性
class Person
{
    friend ostream& operator<<(ostream& out,Person p);

public:
    Person(){}

    Person(int a,int b)
    {
        this->m_a = a;
        this->m_b = b;
    }

private:
    int m_a; 
    int m_b;
};


//只能用全局函数实现 重载<<运算符
ostream& operator<<(ostream& out,Person p)
{
    out<<"m_a = "<<p.m_a<<" m_b = "<<p.m_b;//需要将这个函数声明为友元,访问私有属性
    return out;
}


int main()
{
    Person p(3,5);
    cout<<p<<endl;

    return 0;
}
++运算符重载
#include<iostream>
using namespace std;

class MyInteger
{
    friend ostream& operator<<(ostream& out,MyInteger myint);


public:
    MyInteger(){}

    MyInteger(int m)
    {
        this->m_num = m;
    }


    //重载 前置++ 运算符
    MyInteger& operator++()//用MyInteger& 是为了对自身进行返回,一直对一个数据进行++
    //如果 用MyInteger,则会新创建一个MyInteger对象
    {
        //先进行++运算
        this->m_num++;
        // 在将自身返回
        return *this;
    }


    //重载 后置++ 运算符
    MyInteger operator++(int)//int表示站位参数,可以用于区分前置和后置
    {
        // 先记录最开始的值
        MyInteger temp = *this;
        // 再递增
        this->m_num++;
        // 返回最开始的值
        return temp;//局部对象,只能返回值,不能返回引用
    }
private:
    int m_num;
};


// 重载<<运算符
    ostream& operator<<(ostream& out,MyInteger myint)
    {
        out<<myint.m_num;
        return out;
    }


int main()
{
    MyInteger myint(0);
    cout<<++(++myint)<<endl;//2

    MyInteger myint02(10);
    cout<<(myint02++)<<endl;//10
    cout<<myint02<<endl;//11
    return 0;
}
=运算符重载
#include<iostream>
using namespace std;

class Person
{

public:
    int* m_age;

    Person(){}

    Person(int age)
    {
        this->m_age = new int(age);
    }

    ~Person()
    {
        if (this->m_age != NULL)
        {
            delete this->m_age;
            this->m_age = NULL;
        }
    }


    Person& operator=(Person& p)
    {
        //编译器提供浅拷贝,会出现内存释放冲突
        //this->m_age = p.m_age;

        //1 应该先判断是否有属性在堆区,如果有,先释放干净,然后再深拷贝
        if (this->m_age != NULL)
        {
            delete this->m_age;
            this->m_age = NULL;
        }

        //深拷贝
        this->m_age = new int(*p.m_age);

        return *this;
    }
};


int main()
{

    Person p1(20);

    Person p2;
    p2 = p1;
    cout<<"p2 = "<<*p2.m_age<<endl;

    return 0;
}
类对象=的重载
#include<iostream>
#include<string>
using namespace std;


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

    //重载两个对象的关系运算符
    bool operator==(Person& p)
    {
        if (this->m_age == p.m_age && this->m_name==p.m_name)
        {
            return true;
        }
        else
        {
            return false;
        }
    }


    bool operator!=(Person& p)
    {
        if (this->m_age == p.m_age && this->m_name==p.m_name)
        {
            return false;
        }
        else
        {
            return true;
        }
    }

    int m_age;
    string m_name;
};


int main()
{
    Person p1(20,"zhangsan");
    Person p2(20,"zhangsan");

    if (p1 == p2)
    {
        cout<<"p1和p2 相等"<<endl;
    }
    else
    {
        cout<<"p1和p2 不相等"<<endl;
    }


    if (p1 != p2)
    {
        cout<<"p1和p2 不相等"<<endl;
    }
    else
    {
        cout<<"p1和p2 相等"<<endl;
    }


    cin.get();

    return 0;
}
重载函数调用()运算符——仿函数
#include<iostream>
#include<string>
using namespace std;

class printClass
{
public:
    // 重载函数调用()
    void operator()(string s)
    {
        cout<<s<<endl;
    }
};


class addClass
{
public:
    int operator()(int a,int b)
    {
        return a+b;
    }
};


int main()
{
    printClass p;
    p("hello cpp!");

    addClass a;
    int res = a(3,7);
    cout<<res<<endl;

    //匿名函数
    cout<<addClass()(1,9)<<endl; 


    cin.get();

    return 0;
}

继承

降低代码重复

父类:基类

子类:派生类

#include <iostream>
using namespace std;

class Page
{
public:
    void header()
    {
        cout << "页面头部内容。。" << endl;
    }
    void footer()
    {
        cout << "页面尾部内容。。" << endl;
    }
};

// 继承
class BuyPage : public Page
{
public:
    void content()
    {
        cout << "购买页面" << endl;
    }
};

class Browse : public Page
{
public:
    void content()
    {
        cout << "浏览页面" << endl;
    }
};

int main()
{
    BuyPage buy;
    buy.header();
    buy.footer();
    buy.content();

    Browse browse;
    browse.header();
    browse.footer();
    browse.content();

    return 0;
}
继承方式

在这里插入图片描述

#include <iostream>
using namespace std;

class Father
{
public:
    int m_a = 1;

protected:
    int m_b = 2;

private:
    int m_c = 3;
};

class Son01 : public Father
{
public:
    void test01()
    {
        cout << m_a << endl; // 公共
        cout << m_b << endl; // 保护
        // cout<<m_c<<endl;//不能访问
    }
};

class Son02 : protected Father
{
public:
    void test02()
    {
        cout << m_a << endl; // 变为保护
        cout << m_b << endl; // 变为保护
        // cout<<m_c<<endl;//不能访问
    }
};

class Son03 : private Father
{
public:
    void test03()
    {
        cout << m_a << endl; // 变为私有
        cout << m_b << endl; // 变为私有
        //cout<<m_c<<endl;//不能访问
    }
};
继承中对象属性大小

父类中的所有非静态成员属性都会被子类继承下去,只是某些属性的权限不同

class Father
{
public:
    int m_a = 1;

protected:
    int m_b = 2;

private:
    int m_c = 3;
};

class Son04 : public Father
{
public:
    int m_d = 0;
};

void test()
{
    //私有成员也被继承了,只是访问不到而已
    cout << "size of : "<<sizeof(Son04) << endl;//16
}
继承中的构造和析构顺序
  • 先构造父类,再构造子类
  • 析构顺序相反
class Base
{
    Base()
    {
        cout<<"Base 构造函数。。"<<endl;
    }
    ~Base()
    {
        cout<<"Base 析构函数。。"<<endl;
    }
}
继承同名成员函数
  • 访问子类同名成员函数,直接访问即可
  • 访问父类同名成员函数,需要加作用域
  • 如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有的同名成员函数(包括重载的),需要加作用域
class Base
{
    Base()
    {
        int m_a = 100;
    }
    
public:
    print_hh()
    {
        cout<<"Base: hh"<<endl;
    }
    print_hh(int a)
    {
        cout<<"Base:hh and "<<a<<endl;
    }
}

class Son:public Base
{
    Son()
    {
        int m_a = 200;
    }
    
public:
    print_hh()
    {
        cout<<"Son: hh"<<endl;
    }
}

test()
{
	Son s;
    cout<<"Son: m_a"<<s.m_a<<endl;//200
    cout<<"Base: m_a"<<s.Base::m_a<<endl;//100 父类下的,需要加作用域
    
    s.print_hh();//Son: hh
    s.Base::print_hh();//Base: hh
    
    //如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有的同名成员函数(包括重载的)
    s.Base::print_hh(1);
    
    return 0;
}
继承同名静态成员

子类,直接访问

父类,加作用域

class Base
{
public:
    static int s_a;
    static void fun()
    {
        cout << "Base--fun()" << endl;
    }
};
int Base::s_a = 10;

class Son : public Base
{
public:
    static int s_a;
    static void fun()
    {
        cout << "Son--fun()" << endl;
    }
};
int Son::s_a = 20;

void test01()
{
    Son s;
    // 通过对象访问
    cout << s.s_a << endl;       // 20
    cout << s.Base::s_a << endl; // 10
    s.fun();                     // Son--fun()
    s.Base::fun();               // Base--fun()

    // 通过类名访问
    cout << Son::s_a << endl;       // 20
    cout << Son::Base::s_a << endl; // 10
    Son::fun();
    Son::Base::fun();
}

多继承

class Base1
{
public:
    Base1()
    {
        m_a = 1;
    }
    int m_a;
};

class Base2
{
public:
    Base2()
    {
        m_a = 2;
    }
    int m_a;
};

class Son : public Base1, public Base2
{
public:
    Son()
    {
        m_b = 3;
        m_c = 4;
    }
    int m_b;
    int m_c;
};

void test()
{
    Son s;
    cout << "size of : " << sizeof(s) << endl; // 16

    // 不建议用多继承,因为会出现二义性,必须加作用域用于区分
    cout << "Base1 :" << s.Base1::m_a << endl; // 1
    cout << "Base2 :" << s.Base2::m_a << endl; // 2
}

菱形继承及解决方法(虚继承)

两个派生类继承同一个基类,又有某个类同时继承两个派生类

	 A
   /   \
   B   C
   \   /
     D
#include <iostream>
using namespace std;

class Animal
{
public:
    Animal()
    {
        m_age = 0;
    }
    int m_age;
};

// 虚继承
class Sheep : virtual public Animal
{
public:
    Sheep()
    {
        m_age = 10;
    }
};

// 虚继承
class Tuo : virtual public Animal
{
public:
    Tuo()
    {
        m_age = 20;
    }
};

class SheepTuo : public Sheep, public Tuo
{
};

int main()
{
    SheepTuo st;
    // cout<<st.m_age<<endl;//没有使用虚继承,报错,不明确
    // 作用域区分
    cout << "Sheep : " << st.Sheep::m_age << endl; // 10
    cout << "Tuo : " << st.Tuo::m_age << endl;     // 20

    // 如果我们想明确m_age的继承,只需要继承一个m_age,在内存中只有一份,并且减少内存使用,需要使用虚继承
    cout << st.m_age << endl; // 20

    // m_age在内存中只有一份,Sheep父类和Tuo父类共享内存,指针指向同一个地址
    st.Sheep::m_age = 50;
    cout << st.m_age << endl; // 50
    st.Tuo::m_age = 70;
    cout << st.m_age << endl; // 70

    return 0;
}

多态

虚函数,地址晚绑定,在运行阶段绑定函数地址,如果要实现多态,就需要使用虚函数virtual,如果不加virtual,地址早绑定,在编译阶段确定函数地址

动态多态满足条件

  • 有继承关系
  • 子类重写父类的虚函数
#include <iostream>
using namespace std;

class Animal
{
public:
    //虚函数,地址晚绑定,在运行阶段绑定函数地址,如果要实现多态,就需要使用虚函数virtual
    //如果不加virtual,地址早绑定,在编译阶段确定函数地址
    virtual void speak()
    {
        cout << "animal speaking" << endl;
    }
};

class Cat : public Animal
{
public:
    void speak()
    {
        cout << "cat speaking" << endl;
    }
};

class Dog : public Animal

{
public:
    void speak()
    {
        cout << "dog speaking" << endl;
    }
};

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

int main()
{
    Cat cat;
    // dospeaking(cat); // animal speaking(Animal类中的speak()不加virtual)
    dospeaking(cat); // cat speaking

    Dog dog;
    dospeaking(dog); // dog speaking

    return 0;
}
开闭原则
  • 对扩展进行开放
  • 对修改进行关闭
多态实现计算器
  • 可读性好
  • 维护性高
#include <iostream>
using namespace std;

class AbstractCalcuator
{
public:
    virtual int getResult()
    {
        return 0;
    }
    int number01;
    int number02;
};

class AddCalcuator : public AbstractCalcuator
{
public:
    int getResult()
    {
        return number01 + number02;
    }
};

class SubCalcuator : public AbstractCalcuator
{
public:
    int getResult()
    {
        return number01 - number02;
    }
};

class MulCalcuator : public AbstractCalcuator
{
public:
    int getResult()
    {
        return number01 * number02;
    }
};

int main()
{
    // 实现多态的条件
    // 父类指针或引用指向子类对象
    AbstractCalcuator *cal = new AddCalcuator; // 在堆区创建对象
    cal->number01 = 10;
    cal->number02 = 20;
    cout << "number01 + number02 = " << cal->getResult() << endl;
    // cal指向堆区对象,用完要释放
    delete cal;

    cal = new SubCalcuator; // 在堆区创建对象
    cal->number01 = 10;
    cal->number02 = 20;
    cout << "number01 - number02 = " << cal->getResult() << endl;
    // cal指向堆区对象,用完要释放
    delete cal;

    cal = new MulCalcuator; // 在堆区创建对象
    cal->number01 = 10;
    cal->number02 = 20;
    cout << "number01 * number02 = " << cal->getResult() << endl;
    // cal指向堆区对象,用完要释放
    delete cal;

    return 0;
}

纯虚函数和抽象类

  • 抽象类无法实例化对象

  • 子类必须重写纯虚函数,负责也属于抽象类

#include <iostream>
using namespace std;

class Base
{
public:
    virtual void fun() = 0;
};

class Son : public Base
{
public:
    void fun()
    {
        cout << "fun()函数正在调用。。" << endl;
    }
};

int main()
{
    Base *base = new Son;
    base->fun();

    return 0;
}

虚析构和纯虚析构

问题:多态使用时,如果有子类的属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码

解决:将父类中的析构函数改为虚析构或者纯虚析构

虚析构或纯虚析构是用来解决父类指针释放子类对象

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

拥有纯虚析构函数的类也属于抽象类

#include <iostream>
#include <string>
using namespace std;

class Animal
{
public:
    Animal()
    {
        cout << "Animal构造函数" << endl;
    }
    // 利用虚析构,可以解决 父类指针释放子类对象时不干净的问题
    virtual ~Animal()
    {
        cout << "Animal析构函数" << endl;
    }
    virtual void eat() // 多态的实现条件
    {
        cout << "animal eating..." << endl;
    }
};

class Cat : public Animal
{
public:
    Cat(string name)
    {
        m_name = new string(name); // 堆区创建一个变量
        cout << "Cat构造函数" << endl;
    }
    ~Cat()
    {
        if (m_name != NULL)
        {
            delete m_name;
            m_name = NULL;
            cout << "Cat析构函数" << endl;
        }
    }
    string *m_name; // 一个指针类型的属性

    void eat()
    {
        cout << *m_name << " eating..." << endl;
    }
};

int main()
{

    Animal *animal = new Cat("Tom");
    animal->eat();
    // 父类指针在析构时,不会调用子类的析构函数,导致如果子类有堆区属性,没有成功释放,出现内存泄露
    delete animal;
    return 0;
}

纯虚析构

也是一个抽象类,无法实例化对象

#include <iostream>
#include <string>
using namespace std;

class Animal
{
public:
    Animal()
    {
        cout << "Animal构造函数" << endl;
    }
    // 纯虚析构,类外必须实现
    virtual ~Animal() = 0;
    virtual void eat() // 多态的实现条件
    {
        cout << "animal eating..." << endl;
    }
};
 
Animal::~Animal()
{
    cout<<"Animal析构函数"<<endl;
}

class Cat : public Animal
{
public:
    Cat(string name)
    {
        m_name = new string(name); // 堆区创建一个变量
        cout << "Cat构造函数" << endl;
    }
    ~Cat()
    {
        if (m_name != NULL)
        {
            delete m_name;
            m_name = NULL;
            cout << "Cat析构函数" << endl;
        }
    }
    string *m_name; // 一个指针类型的属性

    void eat()
    {
        cout << *m_name << " eating..." << endl;
    }
};

int main()
{

    Animal *animal = new Cat("Tom");
    animal->eat();
    // 父类指针在析构时,不会调用子类的析构函数,导致如果子类有堆区属性,没有成功释放,出现内存泄露
    delete animal;
    return 0;
}
组装案例
#include <iostream>
using namespace std;

class CPU
{
public:
    virtual void calculate() = 0;
};

class Mermory
{
public:
    virtual void storage() = 0;
};

class VideoCard
{
public:
    virtual void display() = 0;
};

class Computer
{
public:
    Computer(CPU *cpu, Mermory *mermory, VideoCard *videocard)
    {
        m_cpu = cpu;
        m_mermory = mermory;
        m_videocard = videocard;
    }

    void work()
    {
        m_cpu->calculate();
        m_mermory->storage();
        m_videocard->display();
    }

    ~Computer()
    {
        if (m_cpu == NULL)
        {
            delete m_cpu;
            m_cpu = NULL;
        }
        if (m_mermory == NULL)
        {
            delete m_mermory;
            m_mermory = NULL;
        }
        if (m_videocard == NULL)
        {
            delete m_videocard;
            m_videocard = NULL;
        }
    }

private:
    CPU *m_cpu;
    Mermory *m_mermory;
    VideoCard *m_videocard;
};

class IntelCPU : public CPU
{
public:
    void calculate()
    {
        cout << "Intel cpu working.." << endl;
    }
};
class LenovoCPU : public CPU
{
public:
    void calculate()
    {
        cout << "Lenovo cpu working.." << endl;
    }
};
class IntelMermory : public Mermory
{
public:
    void storage()
    {
        cout << "Intel mermory working.." << endl;
    }
};
class LenoveMermory : public Mermory
{
public:
    void storage()
    {
        cout << "Lenovo mermory working.." << endl;
    }
};
class IntelVideoCrad : public VideoCard
{
public:
    void display()
    {
        cout << "Intel videocard working.." << endl;
    }
};
class LenovoVideoCrad : public VideoCard
{
public:
    void display()
    {
        cout << "Lenovo videocard working.." << endl;
    }
};

int main()
{
    // 组装一台电脑
    CPU *cpu01 = new IntelCPU;
    Mermory *mermory01 = new IntelMermory;
    VideoCard *videocard01 = new IntelVideoCrad;

    Computer *computer01 = new Computer(cpu01, mermory01, videocard01);
    computer01->work();
    delete computer01;

    Computer *computer02 = new Computer(new LenovoCPU, new LenoveMermory, new LenovoVideoCrad);
    computer02->work();
    delete computer02;

    return 0;
}

文件操作

  • 文本文件:以文本的ASCII码形式存储在计算机上
  • 二进制文件:以二进制形式存储在计算机上
写文件
#include <iostream>
#include <fstream>
using namespace std;

int main()
{
    // 创建写文件流对象
    ofstream ofs;
    // 指定打开文件的方式
    ofs.open("test01.txt", ios::out);
    // 写内容
    ofs << "姓名:张三" << endl;
    ofs << "性别:男" << endl;
    ofs << "年龄:100" << endl;
    ofs << "今天是2023年11月8日。。。" << endl;
    // 关闭流对象
    ofs.close();

    return 0;
}
读文件
#include <iostream>
#include <fstream>
using namespace std;

int main()
{
    // 创建读文件流对象
    ifstream ifs;
    // 读取文件
    ifs.open("test01.txt", ios::in);
    if (!ifs.is_open())
    {
        cout << "文件打开失败!!" << endl;
    }

    // 第一种
    // char buffer[1024] = {0};
    // while (ifs >> buffer)//按行读取
    // {
    //     cout << buffer << endl;
    // }

    // 第二种
    // char buffer[1024] = {0};
    // while (ifs.getline(buffer, sizeof(buffer))) // 每次按行读取存储在buffer中,每次最大读取sizeof()
    // {
    //     cout << buffer << endl;
    // }

    // 第三种
    // string buffer;
    // while (getline(ifs, buffer))
    // {
    //     cout << buffer << endl;
    // }

    // 第四种
    char c;
    while ((c = ifs.get()) != EOF) // 逐个字符读取,EOF是文件末尾的标志
    {
        cout << c;
    }

    // 流关闭
    ifs.close();

    system("pause");
    return 0;
}
二进制写文件
#include <iostream>
#include <fstream>
using namespace std;

class Person
{
public:
    char name[100];
    int age;
};

int main()
{
    // 创建流对象
    ofstream ofs("test02.txt", ios::out | ios::binary);
    // 打开文件
    // ofs.open("test02.txt",ios::out|ios::binary);

    // 写文件
    Person perosn = {"james", 40};
    ofs.write((const char *)&perosn, sizeof(Person));

    ofs.close();

    return 0;
}
二进制读文件
#include <iostream>
#include <fstream>
using namespace std;

class Person
{
public:
    char name[100];
    int age;
};

int main()
{
    // 创建读文件对象
    ifstream ifs;
    ifs.open("test02.txt", ios::in | ios::binary);
    if (!ifs.is_open())
    {
        cout << "文件打开时失败!!" << endl;
    }
    // 读文件
    Person person;
    ifs.read((char *)&person, sizeof(Person));
    cout << "name: " << person.name << ", age: " << person.age << endl;

    ifs.close();

    system("pause");
    return 0;
}

模版

泛型编程

函数模版,类模版

函数模版
  • 自动类型推导必须推导出一致的数据类型

  • 模版必须要确定出T的数据类型

#include <iostream>
using namespace std;

// 模版函数
template <typename T>
void mySwap(T &a, T &b)
{
    T temp = a;
    a = b;
    b = temp;
}

template <class T>
void printInfo()
{
    cout << "it's great!" << endl;
}

int main()
{
    int a = 10;
    int b = 20;

    // 1 自动类型推导
    mySwap(a, b);
    cout << "a = " << a << endl; // 20
    cout << "b = " << b << endl; // 10

    // 2 显式指定类型
    mySwap<int>(a, b);
    cout << "a = " << a << endl; // 10
    cout << "b = " << b << endl; // 20

    
    
    // 自动类型推导必须要推导出正确的类型
    char c = 'q';
    // mySwap(a,c);//错误的

    // 模版必须要确定出T的类型
    //  printInfo();//错误的
    printInfo<int>(); // 必须显式指定

    system("pause");
    return 0;
}

例子

#include <iostream>
using namespace std;

// 交换两个值的模版函数
template <typename T>
void my_swap(T &a, T &b)
{
    T temp = a;
    a = b;
    b = temp;
}

// 降序排序的模版函数
template <typename T>
void sortArr(T arr[], int len) // 降序
{
    int i = 0;
    while (i < len)
    {
        int max_idx = i;
        for (int j = i + 1; j < len; j++)
        {
            if (arr[j] > arr[max_idx])
            {
                max_idx = j;
            }
        }
        if (max_idx != i)
        {
            my_swap(arr[max_idx], arr[i]);
        }
        i++;
    }
}

//打印不同类型数组的模版函数
template <typename T>
void printArr(T arr[], int len)
{
    int i = 0;
    while (i < len)
    {
        cout << arr[i];
        i++;
    }
    cout << endl;
}

int main()
{
    char charArr[] = "efcsab";
    int lens01 = sizeof(charArr) / sizeof(char);
    sortArr(charArr, lens01);
    printArr(charArr, lens01);

    int intArr[] = {2, 4, 6, 3, 0, 9, 3, 8};
    int lens02 = sizeof(intArr) / sizeof(int);
    sortArr(intArr, lens02);
    printArr(intArr, lens02);

    system("pause");
    return 0;
}
普通函数和函数模板的区别
  • 普通函数调用时可以发生自动类型转换
  • 函数模版调用时,如果是自动类型推导,不会发生自动类型转换;如果是显式指定类型方式,可以发生自动类型转换
#include <iostream>
using namespace std;

int myadd01(int a, int b)
{
    return a + b;
}

template <typename T>
T myadd02(T a, T b)
{
    return a + b;
}

int main()
{
    int a = 1;
    int b = 99;
    char c = 'A';

    cout << myadd01(a, b) << endl;
    cout << myadd01(1, c) << endl;

    cout << myadd02(a, b) << endl;
    // cout << myadd02(a, c) << endl; // 错误,无法推导出一致的数据类型
    cout << myadd02<int>(a, c) << endl;//显式指定,可以发生自动类型转换

    system("pause");
    return 0;
}
  1. 函数模版和普通函数都可以实现,优先调用普通函数
  2. 可以通过空模板参数列表来强制调用函数模板
  3. 函数模版可以发生重载
  4. 函数模版可以产生更好的匹配,优先调用函数模版
#include <iostream>
using namespace std;

void myprint(int a, int b)
{
    cout << "01 普通函数" << endl;
}

template <typename T>
void myprint(T a, T b)
{
    cout << "02 模版函数" << endl;
}

template <typename T>
void myprint(T a, T b, T c)
{
    cout << "03 重载的模版函数" << endl;
}

int main()
{
    int a = 1;
    int b = 99;
    char c = 'A';

    myprint(a, b);   // 01
    myprint<>(a, b); // 02  通过空模板参数列表

    myprint<int>(a, b, c); // 03 重载

    myprint('A', 'a'); // 02

    system("pause"); // 模版函数是更好的匹配

    return 0;
}

既然使用了模版函数,最好不要再提供普通函数,因为会出现二义性

模版局限性和自定义

利用具体化的模版,可以解决自定义类型的通用化

#include <iostream>
using namespace std;

class Person
{
public:
    string name;
    int age;
};

template <typename T>
bool isEqual(T &a, T &b)
{
    if (a == b)
    {
        return true;
    }
    else
    {
        return false;
    }
}

// 利用具体化的Person版本实现,会优先调用
template <>
bool isEqual(Person &a, Person &b)
{
    if (a.name == b.name && a.age == b.age)
    {
        return true;
    }
    else
    {
        return false;
    }
}

int main()
{
    Person p1 = {"zhangsan", 10};
    Person p2 = {"zhangsan", 10};

    cout << isEqual(p1, p2) << endl; // 1 ,会优先调用Person模板函数

    system("pause");
    return 0;
}

类模板

#include <iostream>
using namespace std;

template <class nameType, class ageType>
class Person
{
public:
    Person(nameType name, ageType age)
    {
        m_name = name;
        m_age = age;
    }
    nameType m_name;
    ageType m_age;
};

int main()
{
    Person<string, int> person = {"悟空", 5};
    cout << person.m_name << endl;
    cout << person.m_age << endl;

    system("pause");

    return 0;
}
类模板和函数模板的区别
  1. 类模板没有自动类型推导
  2. 类模板在参数列表中可以有默认参数
template <class nameType, class ageType=int>
class Person
{
public:
    Person(nameType name, ageType age)
    {
        m_name = name;
        m_age = age;
    }
    nameType m_name;
    ageType m_age;
};

int main()
{
    //Person person("八戒", 1000);//错误,不能自动类型推导
    Person<string> person("悟空", 5000);
	return 0;
}
类模板中成员函数创建时机
  • 普通类中的成员函数一开始就可以创建
  • 类模板中的成员函数在调用时才创建
class A
{
public:
    void showA()
    {
        cout<<"A.."
    }
}

template<class T>
class B
{
public:
    T obj;
    //在调用时才创建,因为T不确定
    void fun()
    {
        obj.showA();
    }
}
类模板对象做函数参数
  1. 指定传入的参数类型(通常使用这种方法,直接了当)
  2. 参数模版化
  3. 整个类模版化
#include <iostream>
#include <string>
using namespace std;

template <class T1, class T2>
class Person
{
public:
    Person(T1 name, T2 age)
    {
        this->m_name = name;
        this->m_age = age;
    }
    void show()
    {
        cout << "name:" << this->m_name << " age:" << this->m_age << endl;
    }
    T1 m_name;
    T2 m_age;
};

// 1 指定传入的类型
void printPerson1(Person<string, int> &p)
{
    p.show();
}

// 2 参数模版化
template <class T1, class T2>
void printPerson2(Person<T1, T2> &p)
{
    p.show();
}

// 3 整个类模板化
template <class T>
void printPerson3(T &p)
{
    p.show();
}

int main()
{
    Person<string, int> p("悟空", 200);
    printPerson1(p);

    Person<string, int> p2("八戒", 100);
    printPerson1(p2);

    Person<string, int> p3("如来", 1000);
    printPerson1(p3);

    system("pause");

    return 0;
}
类模板与继承

如果父类是一个类模版,子类需要指定出父类中T的数据类型

  1. 直接制定
  2. 继续使用类模板
#include <iostream>
#include <string>
using namespace std;

template <class T>
class Base
{
public:
    T name;
};

// 1 直接指定父类中的T类型
class Sub : public Base<string>
{
};

// 2 类模板声明父类中的类型,才能继承
template <class T1, class T2>
class Son : public Base<T1>
{
public:
    Son()
    {
        cout << "T1: " << typeid(T1).name() << endl;
        cout << "T2: " << typeid(T2).name() << endl;
    }
    T2 age;
};

int main()
{
    Sub s;
    s.name = "zhang";
    cout << "name:" << s.name << endl;

    Son<string, int> s2;

    system("pause");

    return 0;
}
类模板中成员函数的类外实现
#include <iostream>
using namespace std;

template <class T1, class T2>
class Person
{
public:
    Person(T1 name, T2 age);

    T1 m_name;
    T2 m_age;

    void showPerson();
};

// 构造函数类外实现
template <class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
    this->m_name = name;
    this->m_age = age;
}

// 成员函数的类外实现
template <class T1, class T2>
void Person<T1, T2>::showPerson()
{
    cout << "name: " << this->m_name << " age: " << this->m_age << endl;
}

int main()
{

    Person<string, int> person("zhangsan", 18);
    person.showPerson();

    system("pause");
    return 0;
}
类模板分文件编写

由于类模板中的成员函数创建时机是在调用阶段,在分文件编写时无法编译通过类模版的函数

解决方法:将类模板声明和实现写在一个文件里,约定俗称,以.hpp结尾的文件

person.hpp

#pragma once
#include <iostream>
using namespace std;

template <class T1, class T2>
class Person
{
public:
    Person(T1 name, T2 age);

    T1 m_name;
    T2 m_age;

    void showPerson();
};

// 构造函数类外实现
template <class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
    this->m_name = name;
    this->m_age = age;
}

// 成员函数的类外实现
template <class T1, class T2>
void Person<T1, T2>::showPerson()
{
    cout << "name: " << this->m_name << " age: " << this->m_age << endl;
}

test.cpp

#include <iostream>
using namespace std;

#include "person.hpp"

int main()
{

    Person<string, int> person("zhangsan", 18);
    person.showPerson();

    system("pause");
    return 0;
}
类模板和友元

全局函数作为友元,类外实现的话,需要让编译器提前知道全局函数的存在,需要将代码写在最前面

通常使用类内实现

#include <iostream>
using namespace std;

template <class T1, class T2>
class Person;

// 类外实现
template <class T1, class T2>
void printPerson02(Person<T1, T2> p)
{
    cout << "name " << p.m_name << " age " << p.m_age << endl;
}

template <class T1, class T2>
class Person
{
    // 1 全局函数,类内实现
    friend void printPerson01(Person<T1, T2> p)
    {
        cout << "name " << p.m_name << " age: " << p.m_age << endl;
    }

    // 2 全局函数,类外实现
    // 类外实现,需要让编译提前知道这个函数的存在
    friend void printPerson02<>(Person<T1, T2> p);

public:
    Person(T1 name, T2 age)
    {
        this->m_age = age;
        this->m_name = name;
    }

private:
    T1 m_name;
    T2 m_age;
};

int main()
{
    Person<string, int> p1("zhang", 19);
    printPerson01(p1);

    Person<string, int> p2("li", 25);
    printPerson02(p2);

    system("pause");
    return 0;
}
类模板实例-数组模版

.hpp

#pragma once
#include <iostream>
using namespace std;

template <class T>
class MyArray
{
public:
    MyArray(int capacity)
    {
        this->my_capacity = capacity;
        this->my_size = 0;
        this->my_address = new T[this->my_capacity];
    }

    // 重写拷贝构造函数,避免浅拷贝带来的问题
    MyArray(const MyArray &arr)
    {
        this->my_capacity = arr.my_capacity;
        this->my_size = arr.my_size;
        // 这是编译器提供的浅拷贝
        // this->my_address = arr.my_address;

        // 深拷贝
        this->my_address = new T[arr.my_capacity];
        for (int i = 0; i < this->my_size; i++)
        {
            this->my_address[i] = arr.my_address[i];
        }
    }

    // operator= 防止浅拷贝问题
    MyArray &operator=(const MyArray &arr)
    {
        if (this->my_address != NULL)
        {
            delete[] this->my_address;
            this->my_address = NULL;
            this->my_capacity = 0;
            this->my_size = 0;
        }
        // 深拷贝
        this->my_capacity = arr.my_capacity;
        this->my_size = arr.my_size;
        this->my_address = new T[arr.my_capacity];
        for (int i = 0; i < this->my_size; i++)
        {
            this->my_address[i] = arr.my_address[i];
        }
        return *this;
    }

    ~MyArray()
    {
        if (this->my_address != NULL)
        {
            // delete[] 释放数组中全部元素的内存空间
            delete[] this->my_address;
            this->my_address = NULL;
        }
    }

    // 尾插法
    void push_back(const T &val)
    {
        // 先判断目前的大小是否等于容量
        if (this->my_size == this->my_capacity)
        {
            return;
        }
        // 尾部插入数据,并修改大小
        this->my_address[this->my_size] = val;
        this->my_size++;
    }

    // 尾删法
    void pop()
    {
        if (this->my_size == 0)
        {
            return;
        }
        // 逻辑上的删除
        this->my_size--;
    }

    // 通过下标方式访问 []
    T &operator[](int index)
    {
        return this->my_address[index];
    }

    // 返回数组大小
    int getSize()
    {
        return this->my_size;
    }

    // 返回数组容量
    int getCapacity()
    {
        return this->my_capacity;
    }

private:
    int my_capacity; // 容量
    int my_size;     // 大小
    T *my_address;   // 维护的数组地址
};

.cpp

#include <iostream>
#include <string>
using namespace std;
#include "MyArray.hpp"

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

void printArr(MyArray<int> &arr)
{
    for (int i = 0; i < arr.getSize(); i++)
    {
        cout << arr[i] << endl;
    }
}

void printPersonArr(MyArray<Person> &arr)
{
    for (int i = 0; i < arr.getSize(); i++)
    {
        cout << "name:" << arr[i].m_name << ",age:" << arr[i].m_age << endl;
    }
}

int main()
{
    // 有参构造
    // MyArray<int> arr01(5);
    // // 拷贝构造
    // MyArray<int> arr02(arr01);
    // // =拷贝,先清空,再拷贝
    // MyArray<int> arr03(10);
    // arr03 = arr01;

    MyArray<int> arr01(5);
    for (int i = 0; i < 5; i++)
    {
        // 利用尾插法插入数据
        arr01.push_back(i);
    }
    printArr(arr01);

    cout << "01容量:" << arr01.getCapacity() << endl;
    cout << "01大小:" << arr01.getSize() << endl;

    MyArray<int> arr02(arr01);
    arr02.pop();
    printArr(arr02);
    cout << "02容量:" << arr02.getCapacity() << endl;
    cout << "02大小:" << arr02.getSize() << endl;

    cout << "-----------------" << endl;
    MyArray<Person> arr_person(5);
    Person p1("zhangsan", 12);
    Person p2("lisi", 25);
    Person p3("wangwu", 36);
    Person p4("hhh", 47);
    arr_person.push_back(p1);
    arr_person.push_back(p2);
    arr_person.push_back(p3);
    arr_person.push_back(p4);

    printPersonArr(arr_person);
    arr_person.pop();
    cout << "person大小:" << arr_person.getSize() << endl;

    system("pause");

    return 0;
}

STL标准模板库

Standard Template Library

六大组件

  1. 容器:各种数据结构,vector,list,set,map
  2. 算法:排序,查找
  3. 迭代器:容器和算法之间的胶合剂
  4. 仿函数
  5. 适配器:用来修饰容器或者仿函数或迭代器的
  6. 空间配置器:负责空间配置与管理

Vector

存放内置数据类型
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

void my_print(int val)
{
    cout << val << endl;
}

int main()
{
    vector<int> v;
    v.push_back(10);
    v.push_back(20);
    v.push_back(30);
    v.push_back(40);

    // 第一种遍历方式
    // vector<int>::iterator v_begin = v.begin(); // 起始迭代器,指向第一个位置
    // vector<int>::iterator v_end = v.end();     // 结束迭代器,指向最后一个元素的下一个位置
    // while (v_begin != v_end)
    // {
    //     cout << *v_begin << endl;
    //     v_begin++;
    // }

    
    // 第二种遍历方式
    // for (vector<int>::iterator v_begin = v.begin(); v_begin < v.end(); v_begin++)
    // {
    //     cout << *v_begin << endl;
    // }

    
    // 第三种遍历方式,STL提供的标准遍历算法
    for_each(v.begin(), v.end(), my_print);

    system("pause");
    return 0;
}
存放自定义数据类型
#include <iostream>
#include <string>
#include <vector>
using namespace std;

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

void test01()
{
    // 存放Person对象
    vector<Person> v1;
    Person p1("zhangsan", 1);
    Person p2("lisi", 2);
    Person p3("wangwu", 3);
    v1.push_back(p1);
    v1.push_back(p2);
    v1.push_back(p3);

    for (vector<Person>::iterator it = v1.begin(); it < v1.end(); it++)
    {
        // 两种方式
        cout << "name:" << it->m_name << ",age:" << it->m_age << endl; // 指针
        // cout << "name:" << (*it).m_name << ",age:" << (*it).m_age << endl; // 对象
    }
}

void test02()
{
    // 存放Person对象的地址
    vector<Person *> v2;
    Person p1("zhangsan", 1);
    Person p2("lisi", 2);
    Person p3("wangwu", 3);
    v2.push_back(&p1);
    v2.push_back(&p2);
    v2.push_back(&p3);

    for (vector<Person *>::iterator it = v2.begin(); it < v2.end(); it++)
    {
        // 解引用之后是Person*,对象的地址
        cout << "name:" << (*it)->m_name << ",age:" << (*it)->m_age << endl;
    }
}

int main()
{
    test01();
    cout << "------------" << endl;
    test02();

    system("pause");
    return 0;
}
容器嵌套容器
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<vector<int>> v;

    vector<int> v1;
    vector<int> v2;
    vector<int> v3;

    for (int i = 0; i < 3; i++)
    {
        v1.push_back(i + 1);
        v2.push_back(i + 4);
        v3.push_back(i + 7);
    }

    v.push_back(v1);
    v.push_back(v2);
    v.push_back(v3);

    for (vector<vector<int>>::iterator it = v.begin(); it < v.end(); it++)
    {
        for (vector<int>::iterator sub_it = (*it).begin(); sub_it < (*it).end(); sub_it++)
        {
            cout << *sub_it << "\t";
        }
    }//1    2   3   4   5   6   7   8   9

    system("pause");
    return 0;
}

常用容器

string

  • char* 是一个指针
  • string 是一个类,内部封装了char*,管理这个字符串,是一个char* 型的容器
  • string管理char*所分配到内存,不用担心越界问题
  1. string();
  2. string(const char* s);
  3. string(const string& str);
  4. string(int n,char c);
int main()
{
    // 1
    string s1;

    // 2
    const char *str = "hello cpp";
    string s2(str);

    // 3
    string s3(s2);

    // 4
    string s4(9, 'q');

    return 0;
}
string 赋值操作
    // string& operator=(const char* s);//char*类型字符串 赋值给当前的字符串
    // string& operator=(const string &s);//把字符串s赋给当前的字符串
    // string& operator=(char c) ;//字符赋值给当前的字符串
    // string& assign(const char *s) ;//把字符串s赋给当前的字符串
    // string& assign(const char *s, int n) ;//把字符串s的前n个字符赋给当前的字符串
    // string& assign(const string &s) ;//把字符串s赋给当前字符串
    // string& assign(int n, char c) ;//用n个字符c赋给当前字符串


    string str5;
    str5 = "hello python";

    string str6;
    str6 = str5;

    string str7;
    str7 = 'a';

    string str1;
    str1.assign("hello cpp");

    string str2;
    str2.assign("hello cpp", 5);

    string str3;
    str3.assign(str2);

    string str4;
    str4.assign(3, 'q');
string字符串拼接
#include <iostream>
#include <string>
using namespace std;

int main()
{
   // string& operator+=(const char* str);//重载+=操作符

   // string& operator+=(const char c);//重载+=操作符

   // string& operator+=(const string& str);//重载+=操作符

   // string& append(const char *s);//把字符串s连接到当前字符串结尾

   // string& append(const char *s, int n);//把字符串s的前n个字符连接到当前字符串结尾

   // string& append(const string &s);//同operator+=(const string& str)

   // string& append(const string &s,int pos,int n); //字符s中从pos开始的n个字符连接到字符串结尾

   string str1 = "我";
   str1 += "喜欢编程";
   cout << str1 << endl;
   str1 += '!';
   cout << str1 << endl;

   string str2 = "python,cpp";
   str1 += str2;
   cout << str1 << endl;

   string str3 = "I";
   str3.append(" like ");
   str3.append("program,game", 8);
   cout << str3 << endl;
   str3.append(str2, 7, 3); // 从第7个开始,截取3个字符
   cout << str3 << endl;

   system("pause");

   return 0;
}
string查找,替换
int find(const string& str, int pos = 0) const;//查找str第一次出现位置,从pos开始查找

int find(const char* s, int pos = 0) const;//查找s第一次出现位置,从pos开始查找

int find(const char* s, int pos, int n) const;//从pos位置查找s的前n个字符第一次位置

int find(const char c, int pos = 0) const;//查找字符C第一次出现位置

int rfind(const string& str, int pos = npos) const;//查找str最后一次位置,从pos开始查找

int rfind(const char* s, int pos = npos) const;//查找s最后一次出现位置,从pos开始查找

int rfind(const char* s, int pos, int n) const;//从pos查找s的前n个字符最后一次位置

int rfind(const char c, int pos = ) const;//查找字符C最后一次出现位置

string& replace(int pos, int n, const string& str);//替换从pos开始n个字符为字符串str

string& replace(int pos, int n,const char* s);//替换从pos开始的n个字符为字符串s
string字符串比较

主要用于比较两个字符串是否相等,比较大小意义不大

//大于返回1,小于返回-1,等于返回0
int compare(const string &s) const;//与字符串比较

int compare(const char *s) const;


string str1 = "hello";
string str2 = "abc";
str1.compare(str2);//1
string字符串存取
char& operator[](int n);//[]取字符
char& at(int n);//取字符

string str1 = 'hello cpp';
for(int i=0;i<str1.size();i++)
{
    cout<<str1[i]<<",";
}


for(int i=0;i<str1.size();i++)
{
    cout<<str1.at(i)<<",";
}

str1[0] = 'q';
str1.at(1) = 'z';
string插入和删除
string& insert(int pos, const char* s);//插入字符串

string& insert(int pos, const string& str);//插入字符串

string& insert(int pos, int n, char c);//在指定位置插入n个字符

string& erase(int pos, int n = npos);//删除从Pos开始的n个字符
string子串
string substr(int pos = 0,int n = npos) const;//返回由pos开始的n个字符组成的字符串

Vector

vector和数组相似,也称为单端数组

vector与普通数组的不同之处,vector可以动态扩展

并不是在原空间之后续接空间,而是拷贝到更大的新空间,并删除原空间

vector<T> v;//采用模板实现类实现,默认构造函数

vector(v.begin(),v.end());//将 v[begin(),end()) 区间中的元素拷贝给本身

vector(n,elem);//构造函数将n个elem拷贝给本身

vector(const vector &vec);//拷贝构造函数
构造函数
#include <iostream>
#include <vector>
using namespace std;

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

int main()
{
    // 1 默认无参构造
    vector<int> v1;
    for (int i = 0; i < 10; i++)
    {
        v1.push_back(i);
    }

    print_vector(v1);

    // 2 区间
    vector<int> v2(v1.begin(), v1.end());
    print_vector(v2);

    // 3 n个元素
    vector<int> v3(10, 9);
    print_vector(v3);

    // 4 拷贝
    vector<int> v4(v3);
    print_vector(v4);

    system("pause");

    return 0;
}
赋值
  • =
  • assign()
int main()
{
    // 1 默认无参构造
    vector<int> v1;
    for (int i = 0; i < 10; i++)
    {
        v1.push_back(i);
    }
     
    // 赋值
    vector<int> v5;
    v5 = v1;

    vector<int> v6;
    v6.assign(v5.begin(), v5.end());

    vector<int> v7;
    v7.assign(10, 1);
}
vector容量和大小

容量capacity自动扩容

empty();//判断容器是否为空

capacity();//容器的容量

size();//返回容器中元素的个数

resize(int num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置//如果容器变短,则未尾超出容器长度的元素被删除。

resize(int num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置//如果容器变短,则末尾超出容器长度的元素被删除
#include <iostream>
#include <vector>
using namespace std;

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

int main()
{
    vector<int> v;
    for (int i = 0; i < 10; i++)
    {
        v.push_back(i);
    }
    print_vector(v);                           // 0,1,2,3,4,5,6,7,8,9,
    cout << "是否为空:" << v.empty() << endl; // 0
    cout << "容量:" << v.capacity() << endl;  // 16
    cout << "大小:" << v.size() << endl;      // 10

    // 重设大小,指定填充的值,默认0填充
    v.resize(15, -1);
    print_vector(v);                          // 0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,
    cout << "容量:" << v.capacity() << endl; // 16
    cout << "大小:" << v.size() << endl;     // 15

    //更短的话,多的数直接删除
    v.resize(6);
    print_vector(v);                          // 0,1,2,3,4,5
    cout << "容量:" << v.capacity() << endl; // 16
    cout << "大小:" << v.size() << endl;     // 6

    v.resize(30);
    print_vector(v);                          // 0,1,2,3,4,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    cout << "容量:" << v.capacity() << endl; // 30
    cout << "大小:" << v.size() << endl;     // 30

    system("pause");
    return 0;
}
插入和删除
push back(ele);//尾部插入元素ele

pop_back();//删除最后一个元素

insert(const iterator pos, ele);//选代器指向位置pos插入元素ele

insert(const_iterator pos,int count,ele); //选代器指向位置pos插入count个元素ele//删除迭代器指向的元素erase(const iterator pos);

erase(const_iterator start,const iterator end); //删除选代器从start到end之间的元素

clear();//删除容器中所有元素
#include <iostream>
#include <vector>
using namespace std;

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

int main()
{
    vector<int> v;
    v.push_back(10);
    v.push_back(20);
    v.push_back(30);
    v.push_back(40);
    v.push_back(50);
    print_vector(v); // 10,20,30,40,50,

    // 尾部删除
    v.pop_back();
    print_vector(v); // 10,20,30,40,

    // 插入,第一个参数是迭代器
    v.insert(v.begin(), 0);
    print_vector(v); // 0,10,20,30,40,

    v.insert(v.begin(), 5, -1); //-1,-1,-1,-1,-1,0,10,20,30,40,
    print_vector(v);

    // 删除,参数是迭代器
    v.erase(v.begin());
    print_vector(v); //-1,-1,-1,-1,0,10,20,30,40,

    // 清空1
    v.erase(v.begin(), v.end());
    print_vector(v);

    // 清空2
    v.clear();
    print_vector(v);

    system("pause");
    return 0;
}
数据存取
at(int idx);//返回索引idx所指的数据

operator[];//返回索引idx所指的数据

front();//返回容器中第一个数据元素

back();//返回容器中最后一个数据元素
int main()
{
    vector<int> v;
    v.push_back(10);
    v.push_back(20);
    v.push_back(30);
	
    cout<<v[1]<<endl;//20
    
    cout<<v.at(2)<<endl;//30
    
    cout<<v.front()<endl;//10
    
    cout<<v.back()<<endl;//30
    
    return 0;
}
vector互换容器

swap

int main()
{
    vector<int> v1;
    for (int i = 0; i < 10; i++)
    {
        v1.push_back(i);
    }
    print_vector(v1); // 0,1,2,3,4,5,6,7,8,9,

    vector<int> v2;
    for (int i = -10; i < 0; i++)
    {
        v2.push_back(i);
    }
    print_vector(v2); //-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,

    // 交换容器
    v1.swap(v2);
    print_vector(v1); //-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,
    print_vector(v2); // 0,1,2,3,4,5,6,7,8,9,

    return 0;
}

实际用途:收缩内存空间

// vector<int>(v)是一个匿名对象,交换空间之后,匿名对象指向原来的大容量容器,
// 匿名对象在执行完之后被系统自动回收,起到压缩空间的作用
vector<int>(v).swap(v);
void test()
{
    vector<int> v;
    cout << "容量:" << v.capacity() << endl; // 0
    cout << "大小:" << v.size() << endl;     // 0
    
    for (int i = 0; i < 10000; i++)
    {
        v.push_back(i);
    }
    cout << "容量:" << v.capacity() << endl; // 16384
    cout << "大小:" << v.size() << endl;     // 10000

    // 重设容器大小,容量并不会变,但是只用到非常少的空间
    v.resize(10);
    cout << "容量:" << v.capacity() << endl; // 16384
    cout << "大小:" << v.size() << endl;     // 10

    // 利用swap压缩空间
    // vector<int>(v)是一个匿名对象,交换空间之后,匿名对象指向原来的大容量容器,
    // 匿名对象在执行完之后被系统自动回收,起到压缩空间的作用
    vector<int>(v).swap(v);
    cout << "容量:" << v.capacity() << endl; // 10
    cout << "大小:" << v.size() << endl;     // 10
}
vector预留空间
reserve(int len); //容器预留len个元素长度,预留位置不初始化,元素不可访问
统计容器在插入数据时新开辟内存的次数
void test()
{
    vector<int> v;
    cout << "容量:" << v.capacity() << endl; // 0
    cout << "大小:" << v.size() << endl;     // 0

    int num = 0;
    int *p = NULL; // 指针p指向容器的首元素地址,容器每次扩容都要新开辟内存,首元素地址就会变
    for (int i = 0; i < 10000; i++)
    {
        v.push_back(i);
        if (p != &v[0])
        {
            num++;
            p = &v[0];
        }
    }
    cout << "容量:" << v.capacity() << endl; // 16384
    cout << "大小:" << v.size() << endl;     // 10000
    cout << "开辟次数:" << num << endl;      // 15
}

当容器需要插入很大数据量时

容器开始没有预留空间,插入数据需要自动扩容,如果我们一直开始就知道要插入的元素个数,就可以利用reserve()预留空间,减少空间开辟的次数。上面的代码需要扩容15次,下面的代码只需要扩容1次


void test()
{
    vector<int> v;
    cout << "容量:" << v.capacity() << endl; // 0
    cout << "大小:" << v.size() << endl;     // 0

    v.reserve(10000);
    cout << "容量:" << v.capacity() << endl; // 10000
    cout << "大小:" << v.size() << endl;     // 0

    int num = 0;
    int *p = NULL; // 指针p指向容器的首元素地址,容器每次扩容都要新开辟内存,首元素地址就会变
    for (int i = 0; i < 10000; i++)
    {
        v.push_back(i);
        if (p != &v[0])
        {
            num++;
            p = &v[0];
        }
    }
    cout << "容量:" << v.capacity() << endl; // 16384
    cout << "大小:" << v.size() << endl;     // 10000
    cout << "开辟次数:" << num << endl;      // 1
}

deque

双端数组,头部和尾部都可以操作

deque与vector区别:

  • vector对于头部的插入删除效率低,数据量越大,效率越低。
  • deque相对而言,对头部的插入删除速度回比vector快。
  • vector访问元素时的速度会比deque快,这和两者内部实现有关
  • deque也是支持随机访问的
deque<T> degT;//默认构造形式

deque( beg, end);//构造函数将[beg,end)区间中的元素拷贝给本身

deque(n, elem);//构造函数将n个elem拷贝给本身

deque(const deque &deq);//拷贝构造函数
构造函数
#include <iostream>
#include <deque>
using namespace std;

//限制为只读
void print_deque(const deque<int>& d)
{
    for (deque<int>::const_iterator it = d.begin(); it < d.end(); it++)
    {
        // *it = 10;//不能修改
        cout << (*it) << " ";
    }
    cout << endl;
}

int main()
{
    deque<int> d;
    for (int i = 0; i < 10; i++)
    {
        d.push_front(i); // 前插
    }
    for (int i = 10; i < 20; i++)
    {
        d.push_back(i); // 后插
    }
    print_deque(d);

    //区间
    deque<int> d2(d.begin(), d.end());
    print_deque(d2);

    //10个1
    deque<int> d3(10, 1);
    print_deque(d3);

    //拷贝构造
    deque<int> d4(d3);
    print_deque(d4);

    system("pause");
    return 0;
}
赋值
deque& operator=(const deque &deq);//重载等号操作符
assign(beg, end);//将[beg,end)区间中的数据拷贝赋值给本身
assign(n, elem);//将n个elem拷贝赋值给本身。
大小的操作

deque没有容量的概念,它是不限容量的

deque.empty();//判断容器是否为空
deque.size();//返回容器中元素的个数
deque.resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置//如果容器变短,则末尾超出容器长度的元素被删除
deque.resize(num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置//如果容器变短,则末尾超出容器长度的元素被删除,
插入和删除
两端插入操作:
push_back(elem);//在容器尾部添加一个数据
push_front(elem);//在容器头部插入一个数据
pop_back();//删除容器最后一个数据
pop_front();//删除容器第一个元素

指定位置操作:
insert(pos,elem);//在pos位置插入一个elem元素的拷贝,返回新数据的位置,pos是迭代器对象
insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值
insert(pos, beg,end);//在pos位置插入[beg,end)区间的数据,无返回值,pos,beg,end是迭代器对象
clear();//清空容器的所有数据
erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置,beg,end是迭代器对象
erase(pos);//删除pos位置的数据,返回下一个数据的位置,参数是迭代器
存取
at(int idx);//返回索idx所指的数据
operator[];//返回索引idx所指的数据
front();//返回容器中第一个数据元素
back();//返回容器中最后一个数据元素
排序

STL提供的排序函数

sort(iterator begin,iterator end);//对区间内的元素进行排序

对于支持随机访问的迭代器容器,都可以利用sort算法进行排序

#include <iostream>
#include <deque>
#include <algorithm>
using namespace std;

// 限制为只读
void print_deque(const deque<int> &d)
{
    for (deque<int>::const_iterator it = d.begin(); it < d.end(); it++)
    {
        // *it = 10;//不能修改
        cout << (*it) << " ";
    }
    cout << endl;
}

int main()
{
    deque<int> d;
    for (int i = 0; i < 10; i++)
    {
        d.push_front(i); // 前插
    }
    for (int i = 10; i < 20; i++)
    {
        d.push_back(i); // 后插
    }
    print_deque(d);//9 8 7 6 5 4 3 2 1 0 10 11 12 13 14 15 16 17 18 19
    
    //STL提供的排序算法,默认升序
    sort(d.begin(), d.end());
    print_deque(d);//0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
    
    return 0;
}

stack

栈:先进后出

构造函数:
stack<T> stk;//stack采用模板类实现, stack对象的默认构造形式
stack(const stack &stk);//拷贝构造函数

赋值操作:
stack& operator=(const stack &stk);//重载等号操作符

数据存取:
push(elem);//向栈顶添加元素
pop();//从栈顶移除第一个元素
top();//返回栈顶元素

大小操作:
empty();//判断堆栈是否为空
size();//返回栈的大小
#include <iostream>
#include <stack>
using namespace std;

int main()
{

    stack<int> s;
    s.push(1);
    s.push(3);
    s.push(4);
    s.push(5);

    cout << "size:" << s.size() << endl;

    while (!s.empty())
    {
        cout << s.top() << endl;
        s.pop();
    }

    cout << "size:" << s.size() << endl;

    system("pause");

    return 0;
}

queue

队列:先进先出

构造函数:
queue<T> que;//queue采用模板类实现,queue对象的默认构造形式
queue(const queue &que);//拷贝构造函数

赋值操作:
queue& operator=(const queue &que);//重载等号操作符

数据存取:
push(elem);//往队尾添加元素
pop();//从队头移除第一个元素
back();//返回最后一个元素
front();//返回第一个元素

大小操作:
empty();//判断堆栈是否为空
size();//返回栈的大小

list

链表:链式存储。

  • 适合插入和删除操作
  • 遍历速度没有数组快
  • 占用空间比数组大

STL中的链表是一个双向循环链表

  • 动态分配内存,不会造成内存浪费和溢出
  • 链表插入和删除不用移动大量元素
  • 插入和删除操作不会造成原有的迭代器失效,而在vector中不成立
构造函数
list<T> lst;//list采用采用模板类实现,对象的默认构造形式
list(beg,end);//构造函数将[beg,end)区间中的元素拷贝给本身
list(n,elem);//构造函数将n个elem拷贝给本身
list(const list &lst);//拷贝构造函数。
#include <iostream>
#include <list>
using namespace std;

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

int main()
{
    list<int> li;
    li.push_back(2);
    li.push_back(5);
    li.push_back(7);
    li.push_back(8);
    print_list(li);

    list<int> li2(li.begin(), li.end());
    print_list(li2);

    list<int> li3(10, -1);
    print_list(li3);

    list<int> li4(li3);
    print_list(li4);

    system("pause");
    return 0;
}
赋值和交换
assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身
assign(n, elem);//将n个elem拷贝赋值给本身
list& operator=(const list &lst);//重载等号操作符
swap(lst);//将lst与本身的元素互换
大小操作
size();//返回容器中元素的个数
empty();//判断容器是否为空
resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置//如果容器变短,则末尾超出容器长度的元素被删除。
resize(num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置//如果容器变短,则末尾超出容器长度的元素被删除。
插入和删除
push_back(elem);//在容器尾部加入一个元素
pop_back();//删除容器中最后一个元素
push_front(elem);//在容器开头插入一个元素
pop_front()://从容器开头移除第一个元素
insert(pos,elem);//在pos位置插elem元素的拷贝,返回新数据的位置
insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值.
insert(pos,begend);//在pos位置插入[beg,end)区间的数据,无返回值.
clear();//移除容器的所有数据
erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置
erase(pos);//删除pos位置的数据,返回下一个数据的位置
remove(elem);//删除容器中所有与elem值匹配的元素
存取

链表只能对首尾元素进行存取,不能随机访问

list是双向链表,利用迭代器,++,–

front();//返回第一个元素
back();//返回最后一个元素
反转和排序

不支持随机访问的数据结构,不可以使用标准算法,例如sort

sort(li.begin(),li.end()),不允许的,list不支持随机访问

reverse();//反转
sort();//排序
#include <iostream>
#include <list>
using namespace std;

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

bool my_compare(int a, int b)
{
    return a > b;
}

int main()
{
	// 升序
    li.sort();
    print_list(li);

    // 降序,需要提供一个降序的函数名
    li.sort(my_compare);
    print_list(li);
}
案例-list-自定义类型排序

自定义数据类型需要指定排序规则

#include <iostream>
#include <string>
#include <list>
using namespace std;

class Person
{
public:
    Person(string name, int age, int height)
    {
        this->m_age = age;
        this->m_name = name;
        this->m_height = height;
    }

    string m_name;
    int m_age;
    int m_height;
};

void print_Person(const list<Person> &lp)
{
    for (list<Person>::const_iterator it = lp.begin(); it != lp.end(); it++)
    {
        cout << "name:" << it->m_name << ",age:" << it->m_age << ",height:" << it->m_height << endl;
    }
}

// 指定排序规则
bool person_compare(Person &p1, Person &p2)
{
    // 年龄不相等,按照年龄升序
    if (p1.m_age != p2.m_age)
    {
        return p1.m_age < p2.m_age;
    }
    else // 年龄相等,按照身高降序
    {
        return p1.m_height > p2.m_height;
    }
}

int main()
{
    Person p1("张飞", 20, 180);
    Person p2("赵云", 29, 174);
    Person p3("悟空", 1000, 170);
    Person p4("武松", 29, 173);
    Person p5("张三", 29, 190);

    list<Person> L_person;
    L_person.push_back(p1);
    L_person.push_back(p2);
    L_person.push_back(p3);
    L_person.push_back(p4);
    L_person.push_back(p5);

    print_Person(L_person);

    //指定排序规则
    L_person.sort(person_compare);
    print_Person(L_person);

    system("pause");

    return 0;
}

setmultiset

  • 所有元素在插入时会自动排序
  • setmulti属于关联式容器,底层结构是二叉树
  • set不允许有重复的元素
  • multiset可以有重复的元素
//构造函数
set<T> s;//默认构造
set<T>(const set& s);//拷贝构造
大小
#include <iostream>
#include <set>
using namespace std;

void print_set(set<int> s)
{
    for (set<int>::iterator it = s.begin(); it != s.end(); it++)
    {
        cout << (*it) << " ";
    }
    cout << endl;
}

int main()
{
    set<int> s;
    s.insert(1);
    s.insert(4);
    s.insert(2);
    s.insert(9);
    s.insert(2);  // 不能重复,不会报错
    print_set(s); // 1 2 4 9

    cout << "是否为空:" << s.empty() << endl;
    cout << "大小:" << s.size() << endl;

    set<int> s2;
    s2.insert(-1);
    s2.insert(-5);
    s2.insert(-3);

    // 交换
    s.swap(s2);
    print_set(s); //-5 - 3 - 1

    system("pause");
    return 0;
}
插入和删除
insert(elem);//在容器中插入元素。
clear();//清除所有元素
erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器.
erase(beg,end);//删除区间[beg,end)的所有元素,返回下一个元素的迭代器
erase(elem);//删除容器中值为elem的元素
查找和统计
find(key);//查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
count(key);//统计key的元素个数
setmultiset区别
 #include <iostream>
#include <set>
using namespace std;

int main()
{
    set<int> s;
    pair<set<int>::iterator, bool> ret = s.insert(1); // ret对象,分别返回插入后迭代器的位置和是否插入成功
    if (ret.second)                                   //   取第二个参数
    {
        cout << "插入成功" << endl; // 输出
    }
    else
    {
        cout << "插入失败" << endl;
    }

    ret = s.insert(1); // 第二次插入失败
    if (ret.second)
    {
        cout << "插入成功" << endl;
    }
    else
    {
        cout << "插入失败" << endl; // 输出
    }

    multiset<int> ms;
    ms.insert(1);
    ms.insert(2);
    ms.insert(5);
    ms.insert(2);
    ms.insert(2);
    for (multiset<int>::iterator it = ms.begin(); it != ms.end(); it++)
    {
        cout << (*it) << " ";
    }
    cout << endl; // 1 2 2 2 5

    system("pause");
    return 0;
}
pair队组
pair<type, type> p ( value1, value2 );
pair<type, type> p = make pair( value1, value2 );
#include <iostream>
#include <string>
using namespace std;

int main()
{
    // 1
    pair<string, int> p1("zhangsan", 17);
    cout << p1.first << "," << p1.second << endl;

    // 2
    pair<string, int> p2 = make_pair("lisi", 10);
    cout << p2.first << "," << p2.second << endl;

    system("pause");
}
改变set排序规则

利用仿函数修改排序规则

#include <iostream>
#include <set>
using namespace std;

// 自定义排序规则
class MyCompare
{
public:
    // 仿函数
    bool operator()(int v1, int v2)
    {
        return v1 > v2;
    }
};

int main()
{
    set<int, MyCompare> s;
    s.insert(1);
    s.insert(4);
    s.insert(2);
    s.insert(7);
    s.insert(9);

    for (set<int, MyCompare>::iterator it = s.begin(); it != s.end(); it++)
    {
        cout << (*it) << " ";
    }
    cout << endl; // 9 7 4 2 1

    system("pause");

    return 0;
}

自定义数据类型插入set,需要指定排序规则

#include <iostream>
#include <set>
using namespace std;

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

class personCompare
{
public:
    // 仿函数
    bool operator()(const Person &p1, const Person &p2)
    {
        // 按照年龄降序
        return p1.m_age > p2.m_age;
    }
};

int main()
{
    // 自定义数据类型存储在set容器中,需要指定排序规则
    Person p1("悟空", 300);
    Person p2("刘备", 15);
    Person p3("莎莎", 26);
    Person p4("王四", 72);
    Person p5("呼呼", 50);

    // 需要指定仿函数的类
    set<Person, personCompare> s_person;
    s_person.insert(p1);
    s_person.insert(p2);
    s_person.insert(p3);
    s_person.insert(p4);
    s_person.insert(p5);
    for (set<Person, personCompare>::iterator it = s_person.begin(); it != s_person.end(); it++)
    {
        cout << "name:" << it->m_name << ",age:" << it->m_age << endl;
    }

    system("pause");

    return 0;
}

mapmultimap

map中所有的元素都是pair,根据键排序,不允许重复

multimap可以重复

#include <iostream>
#include <map>
#include <string>
using namespace std;

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

int main()
{
    map<int, string> m;
    // 插入时会按照key排序
    m.insert(pair<int, string>(1, "aaa"));
    m.insert(pair<int, string>(4, "bbb"));
    m.insert(pair<int, string>(3, "ccc"));
    m.insert(pair<int, string>(2, "ddd"));
    print_map(m);

    // 拷贝构造
    map<int, string> m2(m);
    print_map(m2);

    system("pause");
    return 0;
}
大小
size();//大小
empty();//是否为空
swap(m);//交换
插入和删除
insert(elem);//在容器中插入元素
clear();//清除所有元素
erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器
erase(beg, end);//删除区间[beg,end)的所有元素,返回下一个元素的选代器
erase(key);//删除容器中值为kev的元素
    // 插入,4种插入方法
    map<int, int> m3;
    m3.insert(pair<int, int>(1, -1));
    m3.insert(make_pair(2, -2));
    m3.insert(map<int, int>::value_type(3, -3));
    m3[4] = -4;// 但通常是用来取数
    for (map<int, int>::iterator it = m3.begin(); it != m3.end(); it++)
    {
        cout << (*it).first << " : " << it->second << endl;
    }

    // 删除
    m3.erase(3);                    // 按照key删除
    m3.erase(m3.begin());           // 删除迭代器未知的元素
    m3.erase(m3.begin(), m3.end()); // 删除迭代器区间的元素
    m3.clear();
查找和统计
find(key);//查找key是否存在,若存在,返回该键的元素的迭代器; 若不存在,返回set.end();
count(key);//统i计key的元素个数
排序-修改排序规则
// 自定义排序规则
class MyCompare
{
public:
    bool operator()(int v1, int v2)
    {
        return v1 > v2;
    }
};


int main()
{
    // 自定义排序规则-降序
    map<int, int, MyCompare> m_c;
    m_c.insert(make_pair(1, -1));
    m_c.insert(make_pair(3, -3));
    m_c.insert(make_pair(2, -2));
    m_c.insert(make_pair(4, -4));
    for (map<int, int, MyCompare>::iterator it = m_c.begin(); it != m_c.end(); it++)
    {
        cout << "key=" << it->first << ",value=" << it->second << endl;
    }
}
案例-员工分组
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <ctime>
using namespace std;

// 10个员工,随机分配到 测试,研发,美术部门,随机分配工资
// 分别显示每个部门的员工信息

#define CESHI 0
#define YANFA 1
#define MEISHU 2

class Employee
{
public:
    Employee(string name, int salary)
    {
        this->m_name = name;
        this->m_salary = salary;
    }
    string m_name;
    int m_salary;
};

// 创建10名员工
void create_employee(vector<Employee> &v_emp)
{
    string name_arr = "ABCDEFGHIJ";
    for (int i = 0; i < 10; i++)
    {
        int sal = rand() % 10000 + 10000;
        Employee emp(name_arr.substr(i, 1), sal);
        v_emp.push_back(emp);
    }
}

// 打印员工
void print_employee(const vector<Employee> &v_emp)
{
    for (vector<Employee>::const_iterator it = v_emp.begin(); it != v_emp.end(); it++)
    {
        cout << "name:" << it->m_name << ",salary:" << it->m_salary << endl;
    }
}

// 分组到不同部门
void group_department(const vector<Employee> &v_emp, multimap<int, Employee> &m_emp)
{
    for (vector<Employee>::const_iterator it = v_emp.begin(); it != v_emp.end(); it++)
    {
        // 给当前遍历的员工随机分配一个部门
        int dep_id = rand() % 3;                // 0,1,2
        m_emp.insert(make_pair(dep_id, (*it))); // 部门编号-员工
    }
}

// 打印不同部门的员工信息
void print_department_employee(const multimap<int, Employee> &m_emp)
{
    cout << "测试:" << endl;
    multimap<int, Employee>::const_iterator start_ceshi = m_emp.find(CESHI); // 返回迭代器的位置
    int count_ceshi = m_emp.count(CESHI);                                    // 测试部门的员工数量
    for (int index = 0; start_ceshi != m_emp.end() && index < count_ceshi; start_ceshi++, index++)
    {
        cout << "name:" << start_ceshi->second.m_name << ",salary:" << start_ceshi->second.m_salary << endl;
    }

    cout << "研发:" << endl;
    multimap<int, Employee>::const_iterator start_yanfa = m_emp.find(YANFA); // 返回迭代器的位置
    int count_yanfa = m_emp.count(YANFA);                                    // 研发部门的员工数量
    for (int index = 0; start_yanfa != m_emp.end() && index < count_yanfa; start_yanfa++, index++)
    {
        cout << "name:" << start_yanfa->second.m_name << ",salary:" << start_yanfa->second.m_salary << endl;
    }

    cout << "美术:" << endl;
    multimap<int, Employee>::const_iterator start_meishu = m_emp.find(MEISHU); // 返回迭代器的位置
    int count_meishu = m_emp.count(MEISHU);                                    // 美术部门的员工数量
    for (int index = 0; start_meishu != m_emp.end() && index < count_meishu; start_meishu++, index++)
    {
        cout << "name:" << start_meishu->second.m_name << ",salary:" << start_meishu->second.m_salary << endl;
    }
}

int main()
{
    // 利用系统时间设置随机数
    srand((unsigned int)time(NULL));

    vector<Employee> v_emp;
    create_employee(v_emp);
    print_employee(v_emp);

    // 分组到不同部门
    multimap<int, Employee> m_emp;
    group_department(v_emp, m_emp);
    print_department_employee(m_emp);

    system("pause");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值