容器__2018.05.25

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.函数函数局部作用域。无法对这些信息进行封装和隐藏。

事件驱动:在事件还没发生之前就将事件发生后需要执行的方法定义好了,只要事件发生了就执行相应的函数方法。

解决:目标

我要能够内联。

类的名字首字母大写。

小括号又叫函数调用运算符。


主要用在泛型算法中,扩展泛型算法功能。

因为提供了对象的圆括号运算符重载函数。所以它直接调用的是对象的圆括号运算符重载函数。

那都不差,就差在学习方法上。

不能死记硬背,死记硬背在面试官面前连一程都撑不下来。

泛型算法主要针对容器。

排序,对函数功能进行扩展。

方法有问题。

改个类型名字。

传引用进来,避免了临时对象的构造和析构。


猜你喜欢

转载自blog.csdn.net/weixin_40316053/article/details/80455788