1.使用迭代器去遍历容器,而不管迭代器底层数据的类型。
2.c++STL。
STL包含了、容器,空间配置器、泛型算法、类型萃取。memcpy内存拷贝,二倍扩容。
insert
esreas
3.泛型算法:模板实现。只注重方法,不在乎类型。
泛型算法就是操作容器、迭代器的。
给迭代器提供,中括号运算符重载函数。直接通过下标访问元素。
泛型算法,只用迭代器。
typename 不仅可以定义类型,还可以告诉编译器,后面是类型。
写容器要注意:通用!!
听懂了,说不出来,跟不会一样。
提供排序的泛型算法,提供用户提供的自定义排序算法。
回调函数。
定义函数指针。template<typename Iterator,Typename FUNC>
看迭代器迭代的容器里是什么类型。
所谓所谓事件驱动就是回调函数,
事件驱动就是在事件发生之前就将事件的处理罗列出来。例如:
明天吃什么,我并不知道。但是我知道,不管我吃什么,我都会先拿一双筷子,搬个凳子,坐到那块。。。
不管吃什么,只要执行吃的操作都会固定执行这些操作。这就叫做事件驱动。
函数模板,
#include <iostream> #include <time.h> #include <string> #include <list> using std::cout; using std::endl; template<typename T> class myallocator { public: // construct构造 destroy析构 // allocate开辟内存 deallocate释放内存 void construct(void *ptr, const T &val) { new (ptr) T(val); //表示在ptr指向的内存,构造一个值为val的对象 } void destroy(T *ptr) { //自己填 ptr->~T(); } //开辟内存 T* allocate(size_t size) { //malloc return (T*)malloc(size); } //释放内存 void deallocate(void *ptr) { free(ptr); } }; template<typename T, typename Allocator=myallocator<T>> class Vector { public: typedef Vector<T, Allocator> _MyT; //默认构造的vector,底层没分配过内存0 Vector() :mpVec(NULL), mSize(0), mCur(0){} //size表示初始的内存大小,val表示内存初始值 Vector(int size, const T &val = T()) :mSize(size), mCur(size) { mpVec = _allocator.allocate(mSize * sizeof(T)); for (int i = 0; i < mSize; ++i) { _allocator.construct(mpVec + i, val); } } //拷贝构造 Vector(const _MyT &src) :mSize(src.mSize), mCur(src.mCur) { mpVec = _allocator.allocate(sizeof(T)*mSize); for (int i = 0; i < mCur; ++i) { _allocator.construct(mpVec+i, src.mpVec[i]); } } //operator= Vector<T>& operator=(const Vector<T> &src) { if (this == &src) return *this; for (int i = 0; i < mCur; ++i) { _allocator.destroy(mpVec+i); } _allocator.deallocate(mpVec); mpVec = _allocator.allocate(sizeof(T)*mSize); for (int i = 0; i < mCur; ++i) { _allocator.construct(mpVec + i, src.mpVec[i]); } return *this; } ~Vector() { for (int i = 0; i < mCur; ++i) { _allocator.destroy(mpVec + i); } _allocator.deallocate(mpVec); mpVec = NULL; } //末尾添加元素 push_front pop_front O(n) void push_back(const T &val) { if (full()) reSize(); _allocator.construct(mpVec + mCur, val); mCur++; } //末尾删除 void pop_back() { if (empty()) return; --mCur; _allocator.destroy(mpVec + mCur); //把删除的对象一定要析构 } T front()const{ return mpVec[0]; } T back()const{ return mpVec[mCur - 1]; } bool full()const{ return mCur == mSize; } bool empty()const{ return mCur == 0; } T& operator[](int index){ return mpVec[index]; } //内存以2倍方式增长 void reSize() { if (mSize == 0) { mpVec = _allocator.allocate(sizeof(T)); mSize = 1; mCur = 0; } else { T *ptmp = _allocator.allocate(mSize * 2 * sizeof(T)); for (int i = 0; i < mCur; ++i) { _allocator.construct(ptmp + i, mpVec[i]); } mSize *= 2; for (int i = 0; i < mCur; ++i) { _allocator.destroy(mpVec + i); } _allocator.deallocate(mpVec); mpVec = ptmp; } } int size()const{ return mCur; } //定义当前容器的迭代器类型 的作用,就是来遍历容器的(遍历容器底层的数据结构) // typename T class iterator { public: typedef T value_type; iterator(T *p = NULL) { ptr = p; } bool operator!=(const iterator &it) { return ptr != it.ptr; } int operator-(const iterator &it) { return ptr - it.ptr; } void operator++() { ++ptr; } T& operator*() { return *ptr; } T& operator[](int index) { return ptr[index]; } private: T *ptr; }; iterator begin(){ return iterator(mpVec); } iterator end(){ return iterator(mpVec + mCur); } /* vector容器的增加和删除 */ /* C++17次课作业:完成下面的三个接口 在it迭代器的位置,插入val元素 it位置合法:it在【first,last】范围 特殊情况:容器已经满了,插入要先扩容 it位置不合法: 插入失败,抛异常 */ void insert(const iterator &it, const T &val){} /* 删除的容器,按值删除,按迭代器删除 */ void erase(const T &val){} void erase(const iterator &it){} private: T *mpVec; int mSize; int mCur; Allocator _allocator; }; //函数模板,统一进行容器元素的遍历输出 template<typename Container> void showContainer(Container &con) { Container::iterator it = con.begin(); for (; it != con.end(); ++it) { cout << *it << " "; } cout << endl; } //对first和last迭代器区间的所有元素,进行排序,默认小到大 /* 1.mysort这样的函数,参数都接收的是迭代器,这一类函数在C++ STL 里面,大概有70多种,它们统一叫什么名字? 2.把mysort代码写完,里面不能出现具体的类型(int,vector) 3.C++ STL里面也有一个()叫做sort,它当然是用快排(递归实现) 1亿个整数处理的时候,如果sort只采用递归的快排,那么很可能出现栈溢出问题, 为了处理栈溢出的问题,sort做了哪些处理? */ template<typename IteratorT> void mysort(IteratorT first, IteratorT last) { int size = last - first; for (int i = 0; i < size - 1; ++i){ for (int j = 0; j < size - 1 - i; ++j){ if (first[j] > first[j+1]) { IteratorT::value_type tmp = first[j]; first[j] = first[j + 1]; first[j + 1] = tmp; } } } } //重载的泛型算法mysort,提供用户自定义的排序方式 /* 请自行提供两个函数模板,分别比较数据的>和<,并且在main函数中调用一下 该mysort重载版本,分别对容器的元素进行小到大,大到小的排序并且打印出来 事件驱动:逆置了事件的处理和事件发生的时间 */ template<typename T> bool greaterFunc(T a, T b) { return a > b; } template<typename IteratorT, typename FUNC> void mysort(IteratorT first, IteratorT last, FUNC pfunc) { int size = last - first; for (int i = 0; i < size - 1; ++i){ for (int j = 0; j < size - 1 - i; ++j){ if (pfunc(first[j], first[j + 1])) { IteratorT::value_type tmp = first[j]; first[j] = first[j + 1]; first[j + 1] = tmp; } } } } int main(int argc, char* argv[]) { Vector<int> vec1; srand(time(0)); for (int i = 0; i < 20; ++i){ vec1.push_back(rand() % 100); } showContainer(vec1); //默认从小到大排序 vector<int>::iterator list<int>::iteartor mysort(vec1.begin(), vec1.end()); showContainer(vec1); //大到小排 mysort(vec1.begin(), vec1.end(), greaterFunc<int>); //打印输出 showContainer(vec1); //小到大牌 //打印输出 /* 需要一种通用的方式,来遍历任何的容器 operator[]做不到的 通用: 怎么通用?是使用方式通用呢?还是所有容器共用一个迭代器呢? 数组operator[] 链表 二维数组 哈希表 红黑树 迭代器和容器是一一对应的,所以在设计上,把迭代器这个类,设计成容器类型的嵌套类性 */ /* 1.需要在容器里面定义嵌套类型 iterator 2.给容器提供begin方法 iterator begin(); 把容器第0号位元素的迭代器返回回去 3.给容器提供end方法 iterator end(); 把容器末尾元素后继位置的迭代器返回回去 4.给迭代器提供operator!= 5.给迭代器提供operator++() 6.给迭代器提供operator*() Vector<int>::iterator it = vec1.begin(); for (; it != vec1.end(); ++it) { cout << *it << " "; } cout << endl;*/ /* 把vec1里面的偶数都设置为0 Vector<int>::iterator it = vec1.begin(); for (; it != vec1.end(); ++it) { if (*it % 2 == 0) { // it.operator*() *it = 0; //error C2106 : “ = ” : 左操作数必须为左值 } } showContainer(vec1); string str1 = "hello world"; string::iterator it2 = str1.begin(); for (; it2 != str1.end(); ++it2) { cout << *it2; } cout << endl; */ return 0; }
函数不能进行内联,因为在编译时,只通过函数指针并不能确定调用的是哪个函数。
效率太低,无法内联。哪怕函数指针指向的是内联函数,也不能做内联处理。
在c++中使用函数指针存在的两个问题。
1.效率太低,无法内联。
2.函数函数局部作用域。无法对这些信息进行封装和隐藏。
事件驱动:在事件还没发生之前就将事件发生后需要执行的方法定义好了,只要事件发生了就执行相应的函数方法。
解决:目标
我要能够内联。
类的名字首字母大写。
小括号又叫函数调用运算符。
主要用在泛型算法中,扩展泛型算法功能。
因为提供了对象的圆括号运算符重载函数。所以它直接调用的是对象的圆括号运算符重载函数。
那都不差,就差在学习方法上。
不能死记硬背,死记硬背在面试官面前连一程都撑不下来。
泛型算法主要针对容器。
排序,对函数功能进行扩展。
方法有问题。
改个类型名字。
传引用进来,避免了临时对象的构造和析构。