一.可变参数
1.1C语言的可变参数
实现原理:参数个数不确定,但是所有参数保存在栈上是连续的
实现可变参数的条件:函数的参数一定要有特殊的作用(标明后面参数的个数或者类型)
#include <iostream>
#include <stdarg.h>
using namespace std;
int func(int num1,int num2,...)
{
va_list v1;
va_start(v1,num1);
printf("v1 = %d\n",*v1);
int res;
printf("res = %d\n",res);
for(int i = 0;i < num1;i++)
{
res = va_arg(v1,int);
printf("res = %d\n",res);
}
va_end(v1);
return 0;
}
int main(int argc, const char *argv[])
{
func(3,5,6,7);
return 0;
}
1.2c++中的可变参数
不建议使用C语言的可变参数,因为内部原型检查机制不全,不安全
initialize_list 列表初始化
1.3initialize_list 列表初始化
1.3.1本质
用于表示某种特定类型的值的数组,和vector一样,initialize_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)
{
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, const char *argv[])
{
initializer_list<int> init_list{1,2,3,4,5,6};
auto it = init_list.begin();
for(;it != init_list.end();it++)
{
cout << *it << " ";
}
cout << endl;
initializer_list<int> arg{1,2,3,4,5,6};
func(arg);
A a(arg);
return 0;
}
三STL
3.1STL的概念
为了复用性的提升,为了建立数据结构和算法的一套标准,并且降低其间的耦合关系,以及提升各自的独立性(高内聚),交互性操作(相互合作性),c++社群诞生了STL
3.2价值
低层次:一个非常实用的零部件,以及以一个整合的组织去使用
高层次:以泛型思维为基础,系统化,条例清晰的:“软件组织分类学”。
3.3历史
STL 1979 c++ 1971年
1987年 Ada libray c++还未提出template
1992 帕罗奥图 Alex项目-->Stl
1993年 贝尔 ANSI/ISOC++
3.4STL的六大组件
(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 96 104 112 120 128)
3.5容器
3.5.1容器分类
序列式容器(顺序容器):array(c++内建),vector,list,heap,priority-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.3vector容器的内存如何增长
3.5.4vector的数据结构
template <class T,class Alloc = alloc>
class Vector
{
protected:
iterator start; //表示目前使用空间的头
iterator finish; //表示目前使用空间的尾
iterator end_of_storage //表示目前可用空间的尾
};
vector中push_back和emplace_back的区别:push_back插入一次调用一次析构函数,emplace_back是全部插入之后一起调用析构函数,效率更高
#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's create with one num" << endl;
}
Test(const Test &obj)
{
cout << "Test's copy create" << endl;
this->m_a = obj.m_a;
this->m_b = obj.m_b;
}
~Test()
{
cout << "~Test" << endl;
}
};
ostream &operator<<(ostream &out,const Test &obj)
{
out << obj.m_a << endl;
return out;
}
int main(int argc, const char *argv[])
{
vector<int> v1;
vector<string> vs;
vector<vector<string>> vv;
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);
vector<string> v8 = {10,"hello"};
vector<int> v9(100,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);
v3.emplace_back(2);
v3.emplace_back(3);
cout << v3.capacity() << endl;
v3.emplace(v3.begin()+1,Test(5));
v3.resize(10);
v3.resize(10,9);
cout << v3.capacity() << endl;
for(int i = 0;i < v3.size();i++)
{
cout << v3[i] << " ";
}
cout << 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;
v1.push_back(56);
cout << v1.size() << endl;
cout << v1.capacity() << endl;
v1.shrink_to_fit();
cout << v1.size() << endl;
cout << v1.capacity() << 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;
for(auto it = v1.begin();it != v1.end();it++)
{
if((*it % 2) != 0)
{
v1.erase(it);
}
else
{
it++;
}
}
for(auto it = v1.begin();it != v1.end();it++)
{
cout << *it << " ";
}
cout << endl;
return 0;
}
3.5.5list
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's create with one num" << endl;
}
Test(const Test &obj)
{
cout << "Test's copy create" << 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(int argc, const char *argv[])
{
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 << l.front() << endl;
cout << l.back() << endl;
l.resize(15,t4);
func(l);
Test t[5] = {Test{1,"aa"},Test{2,"bb"},Test{3,"cc"},Test{4,"dd"},Test{5,"ee"}};
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.unique();
func(l);
l.sort();
return 0;
}