寒假学习记录三(c++学习笔记)

一、模板

1.模板的概念

模板是泛型编程主要用到的技术。模板就是建立通用的模具,大大提高复用性。就像生活中那种照片、视频的模板,可以直接套用。

c++中主要有两种模板:函数模板和类模板

模板的特点:1.是一个框架,不能直接使用。2.通用性强,但不是万能的。

2.函数模板

作用:建立一个通用模板,其中函数返回类型和形参的类型都可以不具体设置,使用虚拟模板代替

语法:template<typename T>

函数声明或定义

template:声明创建模板,typename:可以用class代替,后面跟着一种数据类型。

T:通用的数据类型,可以替换,一般为大写字母。

eg:

正常实现多个数据交换函数

void swapint(int& a, int& b) {
    int temp;
    temp = a;
    a = b;
    b = temp;
}
void swapd(double& a, double& b) {
    double temp;
    temp = a;
    a = b;
    b = temp;
}
void test() {
    int a = 10;
    int b = 90;
    swapint(a, b);
    cout << "a=" << a << "  b=" << b << endl;
    double c=10.2;
    double d=20.3;
    swapd(c, d);
    cout << "c=" << c << "  d=" << d << endl;
}
int main() {
    test();
    system("pause");
    return 0;
}

对于每一种类型的数据交换,都要专门写一个函数来实现,而c++中除了本来有的int double等类型以外,还有自定义的数据类型,所以全部都写函数代码量居多,所以这时候使用模板来实现。

模板实现数据交换函数

template<typename T>
void myswap(T& a, T& b) {
    T temp;
    temp = a;
    a = b;
    b = temp;
}
void test() {
    int a = 10;
    int b = 90;
    myswap(a, b);
    cout << "a=" << a << "  b=" << b << endl;
    double c=10.2;
    double d=20.3;
    myswap(c, d);
    cout << "c=" << c << "  d=" << d << endl;
}
int main() {
    test();
    system("pause");
    return 0;
}

这就是具体的实现,减少了代码量还有可读性强。

其中函数模板的使用有两种方式:

  1. 直接使用(如上面代码),编译器自动识别类型:函数名(形参)

  1. 指定出类型:函数名<指定类型>(形参)

注意:这两种方法都必须确保通用数据类型正确,才能调用。

函数模板和普通函数的区别:主要是是否有自动类型转换

普通函数是有自动类型转换,在函数模板中,如果使用的是直接使用,自动识别类型,是不存在自动类型转换,但是指定出类型时是存在自动类型转换的。

可以通过空模板参数列表 函数名<>(形参) 来强制调用函数模板

函数模板也可以重载。

模板的局限性以及解决:

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

对于这个函数模板,如果传入的是一个自定义的类person,就会报错,编译器在调用过程无法识别person类=person类的操作,当然可以使用运算符重载解决,但是太过麻烦,可以使用另外一种办法解决,就是具体化参数类型以及函数定义里的内容。

template<>void myswap(person& a, person& b) {
    person temp;
    temp.one = a.one;
    a.one = b.one;
    b.one = temp.one;
}

3.类模板

作用:建立一个通用类模板,其中类的成员的数据类型都可以不具体设置,使用虚拟模板代替

语法:template<typename T>

template:声明创建模板,typename:可以用class代替,后面跟着一种数据类型。

T:通用的数据类型,可以替换,一般为大写字母。

eg:

简单的类模板

template<class A,class B>
class person {
public:
    A name;
    B age;
    void show() {
        cout << "name=" << name << "age=" << age << endl;
    }
};
void test() {
    person<string, int>  p;
    p.name = "小明";
    p.age = 20;
    p.show();
}
int main() {
    test();
    system("pause");
    return 0;
}

当有多种类型需要替代时,每一个虚拟类型之间用,隔开。

在创建对象时用指定出类型的方法确定出类型:类名<具体的类型> 对象名

类模板使用没有自动推导类型,但是在类模板在声明时可以有默认类型设置,就像下面代码一样

template<class A,class B=int>

在类模板中的成员函数是在被调用时才会创建,而普通的类中的成员函数是一开始就创建好的。

类模板实例化的对象作函数参数的传入方式:

  1. 指定传入类型

void test(person<string, int>& p) 
  1. 参数模板化

template<class T1,class T2>
void test(person<T1, T2>& p) {

}
  1. 整个类都模板化

template<class T>
void test(T& p) {

}

类与继承

当子类继承的父类是一个类模板时,在继承过程中必须指定出数据类型,要不然会报错

class test :public person<string, int> {


};

但是如果想灵活指定父类的数据类型,就需要把子类也写成类模板

template<class T1,class T2,class T3>
class test :public person<T2,T3> {

    T3 age;
};
void test1() {
    test<int, string, int> m;
}

T2和T3是继承中指定父类的数据类型,而T1是指定子类自己的数据类,这样就可以灵活应用。

类模板的成员函数类外实现:

template<class T1,class T2>
void person<T1, T2>::show() {

}

必须写出作用域,又以为是类模板所以要加上<T1,T2>,但是编译器在这里识别不了T1和T2,所以要加上template<class T1,class T2>。

类模板分文件编写

因为类模板成员函数是在调用时创建,如果声明和实现分文件写,编译器就找不到。

解决方法:1.直接包含.cpp文件

2.声明和实现写在一个文件,并且改后缀为.hpp

全局函数作类模板的友元的实现:

  1. 直接在类模板内实现(把函数实现写在类内)

  1. 在类外实现,需要提前声明全局函数(在实现类模板前)

二、STL

  1. stl基本概念

STL的产生为了建立数据结构和算法的一套标准。

STL:标准模板库

STL广义上分为容器、算法、迭代器,容器和算法之间通过迭代器无缝衔接

STL几乎所有的代码都使用了函数模板或类模板

  1. STL六大组件

STL大体分为六大组件:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器

容器:各种数据结构,如vector,list,deque等,用来存放数据

算法:各种常用的算法,如sort,find

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

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

适配器:一种用来修饰容器或仿函数或迭代器接口的东西

空间配置器:负责空间的配置和管理

3.STL中的容器、算法、迭代器

容器

用于存放东西

STL容器就是把运用最广泛的数据结构实现出来

常见的数据结构:数组,链表,队列,栈,集合,映射表等

这些容器分为序列式容器和关联式容器:

序列式容器:强调值的排序。容器中每一个元素都有固定位置

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

算法

问题的解法。用有限的步骤解决逻辑或数学上的问题,就叫做算法

算法分为质变算法和非质变算法

质变算法:是指运用过程中会更改区间内元素的内容。如拷贝,替换,删除等

非质变算法:是指运用过程中不会更改区间内元素的内容。如查找,计数,遍历等

迭代器

容器和算法之间的粘合剂

提供一种方法,使之能够依序的寻访某个容器内的各个元素,而又不会暴露容器内的内部表达方式

每一个容器都有自己专属的迭代器

迭代器类似于指定,前期可以把迭代器当作指针看

迭代器种类:

常用迭代器种类为:双向迭代器或随机访问迭代器

4.STL中的容器、算法、迭代器初始

容器分为三种:

  1. 顺序容器:vetor deque list

  1. 关联容器:set map multiset

  1. 容器适配器:stack queue priority_queue

vector

概念:vector数据结构和数组非常像,又叫单端数组。

vector与普通数组的区别:数组是静态空间,而vector可以动态拓展(找到更大的空间,把原来空间的东西复制过去并释放原来的空间)

1.vector的构造函数

功能:创建vector容器

函数原型:

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

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

·vector(n,elem); //将n个elem拷贝给本身

·vector(const vetor &vec); //拷贝构造函数

vector存放内置数据类型并遍历这个容器

算法:for_each

迭代器:vector<int>::iterator

创建vector容器:vector<int> v;

向容器插入数据(10):v.push_back(10);

下面是创建容器、插入数据、遍历容器的简单代码演示:

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>//标准算法头文件
using namespace std;
void myprint(int val) {
    cout << val << endl;
}
void test() {
    //创建一个vector容器
    vector<int> v;
    //插入内置数据
    v.push_back(10);
    v.push_back(20);
    v.push_back(30);
    //通过迭代器访问容器中的数据
    //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;
    //}
    //第三种遍历方式
    for_each(v.begin(), v.end(), myprint);
}
int main() {
    test();
    system("pause");
    return 0;

创建vector容器需要包含头文件<vextor>,第三种遍历方式使用的算法需要包含头文件<algorithm>

第三种遍历方式的底层原理如下图

其实就是前面的方法封装成算法。

vector容器存放其他数据类型

vector容器存放自定义数据类型与存放内置数据类型相同。

也可以容器中嵌套容器,类似于二维数组。:vector<vector<int>> v;

在遍历这个容器时要写两个for循环,外层遍历每一个存放int的容器,内层遍历每个容器内的int类型数据

2.vector的赋值操作

功能:给vector容器赋值

函数原型:

·vector& operator=(const vector &vac); //重载等号操作符,直接=就行

·assign(big,end); //将【big,end】区间的数据拷贝赋值给本身

·assign(n,elem); //将n个elem拷贝赋值给本身

示例:

    vector<int> v;
    vector<int> v1;
    v1 = v;
    vector<int> v2;
    v2.assign(v.begin(), v.end());
    vector<int> v3;
    v3.assign(5, 10);

3.vector容量和大小

功能:对vector容量和大小的操作

函数原型:

·empty(); //判断容器是否为空,如果为空返回1

·capacity(); //返回容器的容量

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

·resize(num); //重新指定容器长度为num,若容器变长,则默认以0填充新位置,若容器变短,则删除末尾位置元素

·resize(num,elem); //重新指定容器长度为num,若容器变长,则默认以elem填充新位置,若容器变短,则删除末尾位置元素

示例:

    //创建一个vector容器
    vector<int> v;
    for (int i = 0; i < 10; i++) {
        v.push_back(i);
    }
    if (v.empty()) {
        cout << "容器为空" << endl;
    }
    else {
        cout << "容器不为空" << endl;
        //输出容器容量
        cout << "容器的容量为" << v.capacity() << endl;
        //输出容器大小(元素个数)
        cout << "容器的大小为" << v.size() << endl;
    }
    //重新设置容器容量,多余的位置用0补充
    v.resize(15);
    //重新设置容器容量,多余的位置用10补充
    v.resize(15, 10);
  1. vector插入和删除

功能:对vector容器进行插入和删除操作

函数原型:

·push_back(a); //尾部插入元素a

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

·insert(const_iterator pos,a); //迭代器指向位置pos插入元素a

·insert(const_iterator pos,int coust,a); //迭代器指向位置pos插入coust个元素a

·erase(const_iterator pos); //删除迭代器指向的元素

·erase(const_iterator start, const_iterator end); //删除迭代器指向的从start到end之间的内容

·clear(); //删除容器中所有元素

示例:

    //创建一个vector容器
    vector<int> v;
    //尾插
    v.push_back(10);
    v.push_back(20);
    v.push_back(30);
    v.push_back(40);
    v.push_back(50);
    //尾删
    v.pop_back();
    //指定位置插入
    v.insert(v.begin(), 100);
    //指定位置插入多个元素
    v.insert(v.begin(),2, 100);
    //指定位置删除
    v.erase(v.begin());
    //指定区间删除与清空容器
    v.erase(v.begin(), v.end());
    v.clear();
  1. vector数据存取

功能:对vector中的数据的存取操作

函数原型:

·at(num); //返回第num个元素的数据

·[num]; //返回第num个元素的数据

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

·back(); //返回容器中最后一个元素的数据

示例:

    //创建一个vector容器
    vector<int> v;
    //尾插
    v.push_back(10);
    v.push_back(20);
    v.push_back(30);
    v.push_back(40);
    v.push_back(50);
    //打印容器第2个元素的数据
    cout << v.at(2) << endl;
    cout << v[2] << endl;//类似数组的操作
    //打印容器第一个和最后一个元素数据
    cout << v.front() << v.back() << endl;
  1. vector互换容器

功能:实现两个容器互换

函数原型:

·swap(v1); //将v1与自身的元素互换

函数比较简单。一般使用这个函数来巧妙的减少空间使用。

首先按下面的代码进行容器的元素的减少

    //创建一个vector容器
    vector<int> v;
    for (int i = 0; i < 1000; i++) {
        v.push_back(i);
      }
    v.resize(3);

一开始的容器大小为1000,容量13000多,而使用resize减少元素数量为3的时候,容器空间(容量)还是13000多。

所以可以使用以下代码来防止这个情况

vector<int>(v).swap(v);

代码解析:vector<int> (v) //创建匿名对象,以v的元素大小为容量创建

.swap() //容器交换

这样之后就可以使v的容量和大小都变为3.

  1. vector预留空间

功能:减少vector在动态扩展容量时的扩展次数

函数原型:

·reserve(num); //容器预留num个元素长度,预留位置不初始化,元素不可访问

set/multiset

1.set基本概念

简介:所有元素都会在插入时自动被排序

本质:set/multiset属于关联式容器,底层结构是用二叉树实现

set和multiset区别:

·set不允许容器中有重复元素

·multiset允许容器中有重复元素

2.set构造和赋值

功能:创建set容器以及赋值

构造:

·set<T> t; //默认构造函数

·set(const set &st); //拷贝构造函数

赋值:

·set& operator=(const set &st); //重载等号操作符,直接使用=

3.set插入和删除

功能:set容器进行插入和删除数据

函数原型:

·insert(a); //在容器中插入元素a

·clear(); //清除所有元素

·erase(const iterator pos); //删除pos迭代器指向的元素,返回下一个元素的迭代器

·erase(const iterator begin,const iterator end); //删除区间迭代器指向区间(begin,end)中所有元素,返回下一个元素的迭代器

·erase(num); //删除容器中值为num的元素;

set容器的创建赋值删除和遍历示例:

    //创建一个vector容器
    set<int> s;
    for (int i = 10; i < 60; i+=10) {
        s.insert(i);
      }
    
    通过迭代器访问容器中的数据
    //set<int>::iterator itBegin = s.begin();//起始迭代器,指向容器第一个元素
    //set<int>::iterator itEnd = s.end();//结束迭代器,指向容器最后一个元素的下一个位置
    遍历容器
 //   //第一种遍历方式
    //while (itBegin != itEnd) {
    //    cout << *itBegin << endl;
    //    itBegin++;
    //}
    //第二种遍历方式
    //for (set<int>::iterator it = s.begin(); it != s.end(); it++){
    //    cout << *it << endl;
    //}
    //第三种遍历方式
    for_each(s.begin(), s.end(), myprint);

set容器的迭代器和遍历使用的算法和vector是相同的。所以过程也差不多。

4.set大小和交换

功能

:统计set容器大小以及交换set容器

函数原型:

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

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

·swap(st); //交换本身与st容器的元素

每个函数功能和操作和vector容器中的基本上都是一样的,就是唯一一点set容器不支持resize(重新指定大小)。

5.set查找和统计

功能:对set容器进行查找数据和统计数据

函数原型:

·find(key); //查找key是否存在,若存在返回该数据元素的迭代器,若不存在,返回set.end().

·count(key); //统计key的元素个数,返回个数(对于set容器只有0或1,而对于multiset就有不同的结果)

queue

1.queue基本概念

概念:queue是一种先进先出的数据结构(是队列容器),它有两个出口

队列容器允许从一端新增元素,从另一端移除元素

队列只有队头和队尾可以被外界使用,因此队列不允许有遍历行为

队列中进数据叫--入队 push

队列中出数据叫--出队 pop

2.queue常用接口

功能:栈容器常用的对外接口

构造函数:

·queue<T> q; //默认构造

·queue(const queue &q); //拷贝构造

赋值操作:

·queue& operator=(const queue& q); //重载等号操作符,直接使用=

数据存取:

·push(a); //往队尾添加元素

·pop(); //从队头移除第一个元素

·back(); //返回最后一个元素

·front(); //返回第一个元素

大小操作:

·empty(); //判读堆栈是否为空

·size(); //返回栈的大小

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值