(c++笔记)第二十九节课:STL

16 篇文章 0 订阅

一 可变参数

1.1 c语言的可变参数

实现原理:参数个数不确定,但是所有参数保存在栈上是连续的。
#include <iostream>
#include <stdarg.h>
using namespace std;
//实现可变参数的条件:函数的参数一定要由特殊的作用(标明后面参数的个数或者类型)
int func(int num1, int num2, ...)  //第一个参数num1是参数的个数或者参数的类型及个数
{
    va_list v1;  //va_list指针,用于va_start可变参数,为char*
    va_start(v1,num1);  //是v1指向后面的可变参数
    printf("v1 = %d\n",*v1);
​
    int res;
    for (int i = 0; i < num1 ; i++)
    {
        res = va_arg(v1, int); //先返回当前元素的值,再把v1往后跳4个字节(sizeof(int)的大小),使得指向下一个参数
        printf("res = %d\n",res);
    }
    va_end(v1);  //释放指针
    return 0;
}
int main()
{
    func(3, 4, 5, 6);
}

1.2 c++11中的可变参数

不建议使用c语言的可变参数,因为内部原型检查机制不全,不安全
initializer_list 列表初始化

1.3 initializer_list 列表初始化

1.3.1 本质

用于表示某种特定类型的值的数组,和vector一样,initializer_list 也是一种模板类型

1.3.2 使用

用{}来初始化变量  比如:std::vector<int> a{1,2,3,4,5};

1.3.3 初始化区别

int c = 3.3;  // 默认类型转换
int b = (int){3.3}  //编译器会给出警告(也有可能是错误)
​

1.3.4 使用

#include <iostream>
#include <stdarg.h>
using namespace std;
class A
{
public:
    A(const initializer_list<int> &t)
    {
        for (auto tmp : t)  //范围for
        {
            cout << tmp << endl;
        }
    }
};
template <typename T>
void func(initializer_list<T> arg)
{
    auto it = arg.begin();
    for (; it != arg.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}
int main(int argc,char *argv[])
{
​
    
    initializer_list<int> init_list{ 1,2,3,4,5,6 };
    //cout << init_list << endl;
    //cout << init_list[0] << endl;
    auto it = init_list.begin();
    for (; it != init_list.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
    func({ 1,2,3,4,5,6,7,8,8,9});
    A a{1,2,3,4,5};
​
    //int a = { 3.3 };
    initializer_list<double> aa = { 3.3 };
    cout << endl;
    
}

二 萃取技术

#include <iostream>
#include <list>
using namespace std;
​
template <typename T>
void printTraitsInfo(const T& t)
{
    cout << "--------------begin-----------" << endl;
​
    cout << "我们要萃取的类型名字是:" << typeid(T).name() << endl;
​
    cout << "is_void = " << is_void<T>::value << endl;                                    //类型是否是void
    cout << "is_class = " << is_class<T>::value << endl;                                  //类型是否是一个class
    cout << "is_object = " << is_object<T>::value << endl;                                //类型是否是 一个对象类型
    cout << "is_pod = " << is_pod<T>::value << endl;                                      //是否普通类(只包含成员变量,不包含成员函数);   POD(plain old data)
    cout << "is_default_constructible = " << is_default_constructible<T>::value << endl;  //是否有缺省构造函数
    cout << "is_copy_constructible = " << is_copy_constructible<T>::value << endl;        //是否有拷贝构造函数
    cout << "is_move_constructible = " << is_move_constructible<T>::value << endl;        //是否有移动构造函数
    cout << "is_destructible = " << is_destructible<T>::value << endl;                    //是否有析构函数
    cout << "is_polymorphic = " << is_polymorphic<T>::value << endl;                      //是否含有虚函数
    cout << "is_trivially_default_constructible = " << is_trivially_default_constructible<T>::value << endl;      //缺省拷贝构造函数是否是可有可无的(没有也行的),返回1表示确实可有可无
​
    cout << "has_virtual_destructor = " << has_virtual_destructor<T>::value << endl;      //是否有虚析构函数
​
    cout << "--------------end-------------" << endl;
​
}
​
class A
{
public:
    A() = default;
    A(A&& ta) = delete;           //移动构造:你要不写delete,系统一般就会认为你有这个成员函数;
    A(const A& ta) = delete;      //拷贝构造 
    virtual ~A() {}
};
class B
{
public:
    int m_i;
    int m_j;
};
class C
{
public:
    C(int t) {} //有自己的构造函数,编译器不会给你提供缺省构造函数
};
​
void func()
{
    printTraitsInfo(int());     //扔一个临时对象进去
    printTraitsInfo(string());
    printTraitsInfo(A());
    printTraitsInfo(B());
    printTraitsInfo(C(1));
    printTraitsInfo(list<int>());
}
​
​
int main(void)
{
    func();
}
​

三 STL

3.1 stl的概念

为了复用性的提升
为了建立数据结构和算法的一套标准,并且降低其间的耦合关系,以及提升各自的独立性(高内聚),交互性操作(相互合作性),c++社群诞生了STL

3.2 价值

低层次:一个非常实用的零部件,以及以一个整合的组织去使用。
高层次:以泛型思维为基础,系统化,条例清晰的:“软件组织分类学”。

3.3 历史

STL  1979 c++ 1971年
1987年 Ada libray c++还未提出template
1992 帕罗奥图 Alex项目-->Stl
1993年 贝尔 ANSI/ISOC++

3.4 STL的六大组件

(1)容器

各种数据结构,vector,list,deque,set,map,用来存放数据。
从实现角度看:是一种类模板,就体积而言 ,占总85%以上。

(2)算法

各种常用的算法,sort,seach,copy,erase....
从实现角度看:function template

(3)迭代器

容器和算法之间的胶合剂,又称为“泛型指针”,一共五种类型,以及其它衍生的变化。
从实现角度看:重载operator*,operator-> operator++,operator--等指针相关操作(class Template)
所有的STL都附带由自己的专属迭代器。

(4)仿函数

行为类似函数,内部重载了()的类或者类模板,一般的函数指针可以狭义的理解为仿函数

(5)配置器(配接器)

一种修饰容器或仿函数或者迭代器接口的东西,例如:stl提供的stack和queue,虽然看似是容器,其实只能算是一种容器配接器,因为他们底层完全借助于deque,所有操作都是由deque供应。
改变functor接口者,称为function adapter(bind,negate(否定),compose(组合))
改变container接口者,称为container adapter
改变iterator接口者,称为iterator adapter                              

(6)空间适配器

负责空间配置与管理(内存池技术)
从实现角度看:配置器实现了一个动态空间配置,空间管理,空间释放 (class Template)
SGI STL   第一级配置器 >128byte
SGI STL   第二级配置器 16个自由链表(8 16 24 32 40 48 56 64 72 80 88 96,104,112,120,128)

3.5 容器

3.5.1 容器分类

序列式容器(顺序容器):array(c++内建),vector,list,heap,prority-heap, slist  deque,stack,queue
					forward_list(c++11)
关联式容器:RB-tree(非公开) set map multiset multimap  hashtable hash-set hash-map 

3.5.2 确定使用哪种容器

通常,使用vector是最好的选择,除非你有更好的理由选择其它容器
1.如果你的程序有很多小的元素,且空间的额外开销很重要,则不要使用list或forward_list。
2.如果程序要求随机访问元素,应该使用vector或者deque
3.如果想在容器的中间插入或者删除元素,则list或forward_list应该是首选
4.如果程序要在头尾位置插入或删除元素,但是不会在中间为hi插入或删除元素,则使用deque。
5.如果想要在程序中随机访问元素,又要在容器的中间插入元素,该怎么办?
   -->答案取决于list或者forward_list中访问元素与vector或deque中插入、删除元素的相对性能。
   一般来说,应用中占主导地位的操作决定了容器类型的选择。
6.如果你还是不确定应该使用哪种容器,那么可以只是用vector或者list公共的操作:使用公共的操作:迭代器
    不使用下标操作,避免随机访问,这样使用两者,都比较方便。

3.5.3 vector容器的内存如何增长

3.5.4 vector的数据结构

template<class T,class Alloc = alloc>
class Vector
{
    protected:
    	iterator start;   //表示目前使用空间的头
    	iterator finish;  //表示目前使用空间的尾
    	iterator end_of_storage //表示目前可用空间的尾
        ...
};

#include <iostream>
#include <vector>
#include <list>
using namespace std;
​
class Test
{
public:
    int m_a;
    int m_b;
public:
    Test()
    {
        cout << "Test" << endl;
    }
    Test(int a):m_a(a)
    {
        cout <<m_a << " Test有一个参数的构造函数" << endl;
    }
    Test(const Test &obj)
    {
        cout << "Test的拷贝构造函数" << endl;
        this->m_a = obj.m_a;
        this->m_b = obj.m_b;
    }
    ~Test()
    {
        cout << "~Test" << endl;
    }
};
​
int main(void)
{
//初始化
    vector<int> v1;
    vector<string> vs;
    vector<vector<string>> vv; //  其元素的类型是string的vector vector的vector
    vector<Test> v3;
    vector<int> v4 = { 1,2,3,4,5,6 }; //列表初始化
    vector<int> v5(v4);
    vector<int> v6(v4.begin() + 1, v4.end());
    vector<int> v7(100);   //预先开辟100个int
    vector<string> v8 = { 10,"hello" }; //10个hello
    vector<int> v9(100, 5);  //预先开辟100个int,全部初始化为5
//插入数据
    v3.reserve(10);
    cout << v3.capacity() << endl;
    v3.push_back(Test(1));  //末尾添加
    v3.push_back(Test(2));
    v3.push_back(Test(3));
    v3.insert(v3.end(), Test(4));  //按位置插入
    
    //v3.emplace_back(1);  //c++11
    //v3.emplace_back(2);
    //v3.emplace_back(3);
    //cout << v3.capacity() << endl;
    //v3.emplace(v3.begin() + 1, Test(5));  //中间插入
    //v3.resize(10); //重新指定容器的长度为10,len > 原来的长度,将没有使用的空间进行初始化,len < 原来的长度,将多余的数据丢弃。
    //v3.resize(10, 9);  //容量扩充为10,多出的部分初始化为9
    //cout << v3.capacity() << endl;
//遍历
    cout << v4.size() << endl;  //元素的个数
    cout << v4.capacity() << endl;  //容器的容量
    for (int i = 0; i < v4.size(); i++)
    {
        cout << v4[i] << " ";
    }
    cout << endl;
​
    for (vector<int>::size_type i = 0; i != 24; i++)
    {
        v1.push_back(i);
    }
    cout << v1.size() << endl;  //元素的个数
    cout << v1.capacity() << endl;  //容器的容量
    v1.reserve(50);
    cout << v1.size() << endl;  //元素的个数
    cout << v1.capacity() << endl;  //容器的容量
    while (v1.size() != v1.capacity())
    {
        v1.push_back(0);
    }
    cout << v1.size() << endl;  //元素的个数
    cout << v1.capacity() << endl;  //容器的容量
    v1.push_back(56);
    cout << v1.size() << endl;  //元素的个数
    cout << v1.capacity() << endl;  //扩充为75,具体依赖于标准库的实现
//归还内存
    v1.shrink_to_fit();
    cout << v1.size() << endl;  //元素的个数
    cout << v1.capacity() << endl;  //扩充为75,具体依赖于标准库的实现
//迭代器遍历vector
​
    /*for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
    {
        cout << *it<<" ";
    }
    cout << endl;*/
​
    for (auto it = v1.begin(); it != v1.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
//删除
    v1.erase(v1.begin());  //删除一个元素
    for (auto it = v1.begin(); it != v1.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
​
    v1.erase(v1.end() - 5, v1.end());
    for (auto it = v1.begin(); it != v1.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
​
    //使用迭代器删除v1中所有的奇数?
    //for (auto it = v1.begin(); it != v1.end();)
    //{
    //  cout << "begin1:" << *v1.begin() << endl;
    //  if ((*it % 2) != 0)
    //  {
    //      v1.erase(it);
    //  }
    //  else
    //  {
    //      it++;
    //  }
    //  cout << "begin2:" << *v1.begin() << endl;
    //  cout << endl;
​
    //}
    //for (auto it = v1.begin(); it != v1.end(); it++)
    //{
    //  cout << *it << " ";
    //}
    //cout << endl;
//assign 赋值(仅顺序容器)
​
    list<string> name;
    vector<const char *> oldstyle;
    //name = oldstyle;(错误)
    name.assign(oldstyle.cbegin(),oldstyle.cend());  //可以将const char*转为string
    return 0;
​
}

3.5.5 list

list好处是每次插入或者删除一个元素,就配置或释放一段空间,因此,对于list空间的运用有绝对的精准,一点也不浪费,而且对于任何位置的插入和删除,list永远是常数时间。
    list采用的迭代器:双向迭代器(bidirectional iterator)
#include <iostream>
#include <list>
#include <string>
using namespace std;
template <typename T>
void func(list<T> &l)
{
    for (auto it = l.begin(); it != l.end(); it++)
    {
        cout << *it <<" ";
    }
    cout << endl;
}
class Test
{
public:
    int m_a;
    string m_name;
public:
    Test()
    {
        cout << "Test" << endl;
    }
    Test(int a,string n):m_a(a), m_name(n)
    {
        cout <<m_a << " Test有一个参数的构造函数" << endl;
    }
    Test(const Test &obj)
    {
        cout << "Test的拷贝构造函数" << endl;
        this->m_a = obj.m_a;
        this->m_name = obj.m_name;
    }
    ~Test()
    {
        cout << "~Test" << endl;
    }
    bool operator==(const Test &t)
    {
        return this->m_a == t.m_a && this->m_name == t.m_name;
    }
};
ostream& operator<<(ostream &out, Test &t)
{
    out << t.m_a <<" "<<t.m_name<<endl;
    return out;
}
int main(void)
{
    list<int> ll(100, 1);
    cout << ll.size() << endl;
    func(ll);
    list<int> l2(ll.begin(), ll.end());
    list<int> l3(l2);
    Test t1(1,"aaa");
    Test t2(2, "bbb");
    Test t3(3, "ccc");
    Test t4(4, "ddd");
    Test t5(5, "eee");
    Test t6(6,"fff");
    list<Test> l;
    l.push_back(t1);
    l.push_back(t2);
    l.push_back(t3);
    l.push_back(t4); //尾插法 
​
    l.push_front(t5); //头插法
    l.push_front(t6);
    l.push_front(Test(7, "ggg"));
    
    l.emplace_front(8, "ppp");
    l.emplace_back(9, "www");
    l.emplace(l.begin(), 10, "ooo");  //在开头插入
    func(l);
​
//删除
    l.pop_back(); //删除最后一个结点
    l.pop_front();//删除第一个结点
    func(l);
    cout << "********************" << endl;
//输出第一个元素
    cout << l.front() << endl;
//输出最后一个元素
    cout << l.back() << endl;
//链表扩充
    l.resize(15, t4);
    func(l);
//对象数组
    cout << "**********" << endl;
    Test t[5] = { Test{1,"a"}, Test{2,"b"}, Test{3,"c"}, Test{4,"d"}, Test{5,"e"} };
    l.insert(l.begin(), t[0]);  //在头部插入一个元素
    l.insert(l.end(), t, t + 5);  //在链表尾部插入一个区间
    func(l);
​
//删除一个区间
    //l.erase(++l.begin(),--l.end());
    //func(l);
//删除某一个位置
    //l.erase(l.begin());
    //func(l);
//删除一个具体的元素
    //l.remove(t4);
    //func(l);
//c++20:unique
    l.unique(); //相邻重复的全部删除
    func(l);
/*重载*/
    l.sort();
    
    return 0;
​
}
​

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值