施磊c++基础4

学习C++类库的编程基础

4.1 理解函数模板

C++模板的意义:对类型也可以参数化

函数模板
模板的实例化
模板函数

模板类型参数
模板非类型参数

模板的实参推演
模板的特例化(专用化)
模板函数,模板的特例化,非模板函数的重载关系

对于

int sum(int a, int b){return a+b;}

模板例子:

#include<iostream>
using namespace std;
template<typename T>

bool compare(T a, T b) //是一个函数模板
{
	cout << "template compare" << endl;
	return a > b;
}
/*
在函数调用点,编译器用用户指定的类型,从原模板实例化一份函数代码出来
bool compare<int>(int a,int b)
{
}
*/

int main(){

	compare<int>(10, 20); 

	return 0;
}

这使得我们可以只用一套代码实现一套逻辑,至于具体的实例化,交给编译器就行了

此外,模板还有实例推演:
可以根据用户传入的实参类型,来推导出模板类型参数的具体类型

例如可以直接conpare(int a,int b)而省略掉<int>

但是如果是compare(10,12.5)呢?
由于我们之前定义的是bool compare(T a, T b),a与b是同一类型,此时编译器无法判断,就会报错。

我们可以直接指定compare<int>(10,12.5),那么他就会强转为int类型,并且抛出一个警告,说明在T类型转换时可能会发生数据丢失。

紧接着又出现了一个问题
对于compare("aaa","bbb")
编译器推导出的类型是const char * 类型
那么a>b实际上对比的是地址大小,这没有意义

所以我们要对函数模型。提供const char *类型的特例化版本

#include<iostream>
using namespace std;
template<typename T>

bool compare(T a, T b) //是一个函数模板
{
	cout << "template compare" << endl;
	return a > b;
}
/*
在函数调用点,编译器用用户指定的类型,从原模板实例化一份函数代码出来
bool compare<int>(int a,int b)
{
}
*/

template<>//特例化
bool compare(const char* a, const char* b)
{
	cout << "const char * compare" << endl;
	return strcmp(a, b) > 0;

}

int main(){

	compare<int>(10, 20); 
	compare("aaa", "bbb");
	return 0;
}

结果
在这里插入图片描述

其实,在STL中,有很多的对于字符串类型的特例化版本

4.2 理解类模板

使用类模板实现栈

#include<iostream>
using namespace std;

template<typename T>
class SeqStack
{
public:
	SeqStack(int size = 10)
		:_pstack(new T[size])
		, _top(0)
		, _size(size)
	{}


	~SeqStack()
	{
		delete[]_pstack;
		_pstack = nullptr;
	}

	SeqStack(const SeqStack<T>& stack)
		:_top(stack._top)
		,_size(stack._size)
	{
		_pstack = new T[_size];
		for (int i = 0; i < _top; ++i)
		{
			_pstack[i] = stack._pstack[i];
		}


	}
	SeqStack<T>& operator = (const SeqStack<T>& stack)
	{
		if (this == &stack) return *this;

		delete[] _pstack;

		_top = stack._top;
		_size = stack._size;
		_pstack = new T[_size];
		for (int i = 0; i < _top; ++i)
		{
			_pstack[i] = stack._pstack[i];
		}
		return *this;

	}

	void push(const T& val)
	{
		if (full()) expand();

		_pstack[_top++] = val;
	}
	void pop()
	{
		if (empty()) return;
		--_top;
	}
	T top() const
	{
		if (empty())
			throw "stack is empty";
		return _pstack[_top - 1];
	}
	bool full() const
	{
		return _top == _size;
	}

	bool empty() const
	{
		return _top == 0;
	}

	



private:
	T* _pstack;
	int _top;
	int _size;

	//顺序栈底层数组按照2倍方式扩容
	void expand()
	{
		T* ptmp = new T{ _size * 2 };
		for (int i = 0; i < _top; ++i)
		{
			ptmp[i] = _pstack[i];
		}
		delete[]_pstack;

		_pstack = ptmp;
		_size *= 2;
	}

};
int main(){
	SeqStack<int> S;
	S.push(10);	
	S.push(20);	
	S.push(30);
	cout << S.top();
	S.pop();
	cout << S.top();
	return 0;
}
4.3 实现STL里vector代码
#include<iostream>
using namespace std;

template<typename T>
class vector
{
public:

	vector(int size = 10)
	{
		_first = new T[size];
		_last = _first;
		_end = _first + size;
	}
	~vector()
	{
		delete[] _first;
		_first = _last = _end = nullptr;
	}
	
	vector(const vector<T>& rhs)
	{
		_first = new T[];
		int size = rhs._end - rhs._first;
		
		int len = rhs._last - rhs._first;

		for (int i = 0; i < len; ++i)
		{
			_first[i] = rhs._first[i];
		}

		_last = _first + len;
		_end = _first + size;
	}

	vector<T>& operator=(const vector<T>& rhs)
	{
		if (this == &rhs)
			return *this;
		delete[]_first;
		
		int size = rhs._end - rhs._first;

		int len = rhs._last - rhs._first;

		for (int i = 0; i < len; ++i)
		{
			_first[i] = rhs._first[i];
		}

		_last = _first + len;
		_end = _first + size; 


		return *this;
	}

	void push_back(const T& val)
	{
		if (full())
			expend();
		*_last++ = val;

	}

	void pop_back()
	{
		if (empty())
			return;
		--_last;
	}

	T back()const//返回容器末尾的元素值
	{
		return *(_last - 1);
	}

	bool full()const { return _last == _end; }
	bool empty()const { return _first == _last; }
	int size()const { return _last - _first; }
private:
	T* _first;//指向数组起始位
	T* _last;//指向数组中有效元素的后继位置
	T* _end;//指向数组空间的后继位置

	void expend()//容器的二被扩容接口
	{
		int size = _end - _first;
		T* ptmp = new T[2 * size];
		for (int i = 0; i < size; i++)
		{
			ptmp[i] = _first[i];
		}

		delete[]_first;
		_first = ptmp;
		_last = _first + size;
		_end = _first + 2 * size;


	}

};
	
int main(){
	vector<int> v;

	for (int i = 0; i < 20; i++)
	{
		v.push_back(rand() % 100);
		cout << v.back()<< " ";

	}
	cout << endl;
	while (!v.empty())
	{
		cout << v.back() << " ";
		v.pop_back();
	}
	return 0;
}

虽然我们实现了一个vector容器
但是其实距离真正的vector还差很多
主要是差一个空间配置器allocator

4.4 理解容器空间配置器allocator的重要性

在上一节的基础上:


class Test
{
public:
	Test() { cout << "Test()" << endl; }
	~Test() { cout << "~Test()" << endl; }
};




	
int main(){
	vector<Test> v;
	return 0;
}

明明是空容器,结果创建了10个Test对象

在这里插入图片描述

  1. 需要把内存开辟和对象构造分开处理
  2. 析构容器有效元素,然后释放_first指针指向的内存

当使用pop()的时候,仅仅是_last–了,原来Test指向的内存并没有析构掉,当创建新对象时,原来的内存就丢掉了。

  1. 只需要析构对象,要把对象的析构和内存释放分离开

空间配置器allocator做四件事情

  • 内存开辟/内存释放 对象构造/对象析构

template<typename T>
class Allocator
{
	T* allocator(size_t size)//负责内存开辟
	{
		return (T*)malloc(sizeof(T) * size);
	}

	void deallocator(void* p)//负责内存释放
	{
		free(p);
	}

	void construct(T* p, const T& val)//负责对象构造
	{
		new (p) T(val);//定位new
	}

	void destroy(T* p)//负责对象的析构
	{
		p->~T();//
	}

};

和c++内置的allocator空间配置器一样

#include<iostream>
using namespace std;

template<typename T>
class Allocator
{
	T* allocate(size_t size)//负责内存开辟
	{
		return (T*)malloc(sizeof(T) * size);
	}

	void deallocator(void* p)//负责内存释放
	{
		free(p);
	}

	void construct(T* p, const T& val)//负责对象构造
	{
		new (p) T(val);//定位new
	}

	void destroy(T* p)//负责对象的析构
	{
		p->~T();//
	}

};

/*
容器底层,内存开辟,内存释放,对象构造和析构,都通过allocator来实现
*/
template<typename T,typename Alloc = Allocator<T>>
class vector
{
public:

	vector(int size = 10,const Alloc &alloc=Allocator<T>())
	{
		_first = _allocator.allocate(size);
		_last = _first;
		_end = _first + size;
	}
	~vector()
	{
		for (T* p = _first; p != _last; ++p)
		{
			//把_first指针指向的数组的有效元素进行析构操作
			_allocator.destroy(p);


		}
		//释放堆上的数组内存
		_allocator.deallocator(_first);
		_first = _last = _end = nullptr;
	}

	vector(const vector<T>& rhs)
	{
		//_first = new T[];

		_first = _allocator.allocate(size);

		int size = rhs._end - rhs._first;

		int len = rhs._last - rhs._first;

		for (int i = 0; i < len; ++i)
		{
			//_first[i] = rhs._first[i];
			_allocator.construct(_first + 1, rhs._first);
		}

		_last = _first + len;
		_end = _first + size;
	}

	vector<T>& operator=(const vector<T>& rhs)
	{
		if (this == &rhs)
			return *this;
		delete[]_first;

		int size = rhs._end - rhs._first;

		int len = rhs._last - rhs._first;

		for (int i = 0; i < len; ++i)
		{
			_first[i] = rhs._first[i];
		}

		_last = _first + len;
		_end = _first + size;


		return *this;
	}

	void push_back(const T& val)
	{
		if (full())
			expend();
		*_last++ = val;

	}

	void pop_back()
	{
		if (empty())
			return;
		--_last;
	}

	T back()const//返回容器末尾的元素值
	{
		return *(_last - 1);
	}

	bool full()const { return _last == _end; }
	bool empty()const { return _first == _last; }
	int size()const { return _last - _first; }
private:
	T* _first;//指向数组起始位
	T* _last;//指向数组中有效元素的后继位置
	T* _end;//指向数组空间的后继位置
	Alloc _allocator;


	void expend()//容器的二被扩容接口
	{
		int size = _end - _first;
		T* ptmp = new T[2 * size];
		for (int i = 0; i < size; i++)
		{
			ptmp[i] = _first[i];
		}

		delete[]_first;
		_first = ptmp;
		_last = _first + size;
		_end = _first + 2 * size;


	}

};



	
int main(){
	

	return 0;
}
C++多线程编程中,施磊的代码示例提供了一个经典的“哲学家进餐问题”的实现[^1]。该问题的核心在于多个线程(代表哲学家)需要同时获取两个资源(左右两边的叉子),这可能导致死锁。施磊的实现中,通过使用`std::mutex`来模拟叉子,并在`wantsToEat`方法中使用`std::unique_lock`来确保线程安全。 以下是一个简化的C++多线程资源管理示例,适用于类似场景: ```cpp #include <iostream> #include <thread> #include <vector> #include <mutex> #include <functional> class DiningPhilosophers { public: const int n = 5; std::vector<std::unique_ptr<std::mutex>> mtxs; DiningPhilosophers() { for (int i = 0; i < n; i++) { mtxs.push_back(std::make_unique<std::mutex>()); } } void wantsToEat(int philosopher, std::function<void()> pickLeftFork, std::function<void()> pickRightFork, std::function<void()> eat, std::function<void()> putLeftFork, std::function<void()> putRightFork) { std::unique_lock<std::mutex> llock(*mtxs[philosopher]); std::unique_lock<std::mutex> rlock(*mtxs[(philosopher + n - 1) % n]); pickLeftFork(); pickRightFork(); eat(); putLeftFork(); putRightFork(); } }; int main() { DiningPhilosophers dp; auto pickLeft = [](int id) { std::cout << "Philosopher " << id << " picked up left fork.\n"; }; auto pickRight = [](int id) { std::cout << "Philosopher " << id << " picked up right fork.\n"; }; auto eat = [](int id) { std::cout << "Philosopher " << id << " is eating.\n"; }; auto putLeft = [](int id) { std::cout << "Philosopher " << id << " put down left fork.\n"; }; auto putRight = [](int id) { std::cout << "Philosopher " << id << " put down right fork.\n"; }; std::vector<std::thread> threads; for (int i = 0; i < 5; ++i) { threads.emplace_back([&dp, i]() { dp.wantsToEat(i, [&]() { pickLeft(i); }, [&]() { pickRight(i); }, [&]() { eat(i); }, [&]() { putLeft(i); }, [&]() { putRight(i); }); }); } for (auto& t : threads) { t.join(); } return 0; } ``` ### C++资源推荐 1. **书籍** - 《C++ Primer》:适合初学者和有一定经验的开发者,内容详尽。 - 《Effective Modern C++》:Scott Meyers的经典之作,深入探讨C++11/14的新特性。 - 《C++ Concurrency in Action》:Anthony Williams撰写,专注于C++并发编程。 2. **在线教程与文档** - [CppReference](https://en.cppreference.com/):权威的C++参考文档,涵盖标准库和语言特性。 - [LearnCPP](https://www.learncpp.com/):适合初学者的C++教程网站。 - [GeeksforGeeks C++](https://www.geeksforgeeks.org/c-plus-plus/):提供大量C++示例和算法实现。 3. **视频课程** - **B站**:搜索“施磊 C++”,可以找到相关的教学视频,涵盖基础语法、面向对象、STL、多线程等。 - **Coursera**:提供由大学教授讲授的C++课程,如“C++程序设计”。 - **Udemy**:有大量实战项目导向的C++课程,如“C++17 Fundamentals”。 4. **开源项目与代码库** - **GitHub**:搜索关键词“C++ 项目”或“C++ 示例”,可以找到大量开源项目,如游戏引擎、算法库、图形渲染等。 - **Boost库**:C++的扩展库,包含大量实用的模板和工具类,适合进阶学习。 5. **社区与论坛** - **Stack Overflow**:遇到具体问题时,搜索相关标签(如`c++`)可以找到解决方案。 - **Reddit的r/cpp**:活跃的C++讨论区,适合交流经验和获取最新资讯。 - **C++论坛**:一些中文技术社区(如优快云、知乎)也有专门的C++讨论区。 ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值