项目学习地址:【牛客网C++服务器项目学习】
简化编程人员的工作,使得程序员编写的自定义类能够被复用到其他程序代码中去。
使用方法:
1 - 声明和定义
template <typename T> // typename T 该位置可以定义不止一个(map的参数就是两个)
class MyClass
{
//
int sum();
};
类模板中的成员函数,放在模板类外去定义的写法
template <typename T>
int MyClass<T>::sum()
{
//
}
2 - 类的使用
MyClass<int> obj;
MyClass<int> obj();
模板类的声明、定义以及使用就是如此啦。其实我们实际开发中经常会使用到C++内置的几个模板类:vector、map这几个STL容器函数,都是基于模板类编写出来的。
在之前Linux C的程序编写中,当某个函数执行发生错误后,我们调用perror
函数在终端打印输出错误信息。在Linux C++中,对异常错误的处理,则是使用另一套机制:
class MyClass
{
//声明
static int mem;
static int func1();
};
//定义。无需再加上static关键字
int MyClass::mem = 10;
int MyClass::func1()
{
//函数体
}
在day08的学习中,掌握了三种方式常用函数的使用方式。牛客网这个项目中,在locker.h
中把三种同步方式都封装了,有必要重新总结一下三种同步方式各自的优缺点以及使用场景,这样可以帮助我们更好的理解各自在程序代码中发挥的作用个,甚至让我们可以对代码做出自己的修改
互斥锁:
变量类型:pthread_mutex_t
上锁:
解锁:
互斥锁使用起来简单、直观,对临界区的保护是上锁和解锁两种状态。但是,正是因为如此,使用互斥锁对临界区进行保护的话,从进程/线程的角度出发,看到的只有临界区 可进入/不可进入两种状态,进入临界区后,哪些资源可以用,哪些资源不能用,互斥锁是做不到的。
当然,我们可以在进入临界区后,通过一些if语句代码进行判断,但是这样会增加编程的复杂性,这正是互斥锁的局限性,也正是因为互斥锁不能表征临界区资源的具体访问内容,才会有条件变量和信号量的出现。
所以说,互斥锁的使用场景应该是在简单的场景上,进入临界区后,就开始访问资源。要么是1, 要么是0的简单情况。
条件变量:
变量类型:pthread_cond_t
条件阻塞:
条件激活:
条件变量一定和配合着互斥锁来使用的,目的就是弥补互斥锁功能的单一性。在条件不满足的时候阻塞进程/线程(阻止进程/线程访问临界区资源),在临界区资源满足访问要求时,激活一个或者全部进程/线程去访问资源。相较于互斥锁,不需要在临界区内使用if代码去判断,在满足访问要求后,直接使用signal函数激活一个进程/线程即可。
条件变量就像是在互斥锁机制的基础上,打了一个补丁。互斥锁能够做的事情,条件变量也能做,互斥锁做不到的,条件变量兴许能够做。
信号量:
信号量其实就是一个计数器,也是一个整数。每一次调用wait操作将会使semaphore值减一,而如果semaphore值已经为0,则wait操作将会阻塞。每一次调用post操作将会使semaphore值加一。
信号量与线程锁、条件变量相比还有以下几点不同:
1)锁必须是同一个线程获取以及释放,否则会死锁。而条件变量和信号量则不必。
2)信号的递增与减少会被系统自动记住,系统内部信号量的底层有一个计数器保存计数数值,不必担心会丢失,而唤醒一个条件变量时,如果没有相应的线程在等待该条件变量,这次唤醒将被丢失。
互斥锁能做的事情,信号量能不能做?能。初始化信号量将其数值设置为1即可。
条件变量能做的事情,信号量能不能做?能。信号量的数值可以表征可用资源的数量,让进程/线程得以知道哪些资源可以访问,哪些资源不能访问。
所以,我的结论是信号量是三者之中最完善的机制。还没想明白为什么需要封装其他同步机制。
希望学到后面我能回答这个问题。
想必很多人都看过“头文件中用到的 #ifndef/#define/#endif 来防止该头文件被重复引用”。但是是否能理解“被重复引用”是什么意思?头文件被重复引用了,会产生什么后果?是不是所有的头文件中都要加入#ifndef/#define/#endif 这些代码?
其实“被重复引用”是指一个头文件在同一个cpp文件中被include了多次,这种错误常常是由于include嵌套造成的。如:存在a.h文件#include "c.h"而此时b.cpp文件导入了#include “a.h” 和#include "c.h"此时就会造成c.h重复包含。
头文件被重复引用引起的后果:
是不是所有的头文件中都要加入这些代码?
使用方法:
#ifndef __XXX_H__ //意思是 "if not define __XXX_H__" 也就是没包含XXX.h
#define __XXX_H__ //就定义__XXX_H__
... //此处放头文件中本来应该写的代码
#endif //否则不需要定义
此外,随着编译器的更新,出现了一种新的写法:#pragma once。这种写法只能被较高版本的编译器认可,所以为了保险起见,还是老老实实的写#ifndef吧