一 可变参数
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 使用
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; }