现在的位置: 首页 > 综合 > 正文

C++学习笔记一

2018年02月19日 ⁄ 综合 ⁄ 共 3071字 ⁄ 字号 评论关闭

一:异常处理

       异常是程序执行期间预期发生的错误。它是程序正常功能之外程序无法解决的问题。通常问题的检测和问题的处理相互分开。在问题的检测端通过throw抛出异常对象(或在try语句中通过调用抛出异常的函数),问题处理端通过catch捕获抛出异常的对象。这里传递的对象可以是对象的副本或者引用和指针,但是指针不应该是指向局部对象的指针,否则抛出异常后,该对象就被释放了,就无法再使用该对象了,指针也就变成了悬垂指针了。此外,这里的对象可以是标准库函数自带的异常类,也可以自定义的类。如下面的例子就编写了一个除数为0的异常处理机制。

error.h

#ifndef ERROR
#define ERROR
#include <string>
using namespace std;

class Error{
public:
	virtual void showErrorMsg() = 0;
	virtual ~Error(){}
};

class MyExceptionZero:public Error{
public:
	MyExceptionZero(string &s):msg(s){}
	void showErrorMsg(){cout << msg <<endl;}
private:
	string msg;
};
#endif

Main

#include "Queue.h"
#include "Queue.cpp"       //  包含编译模型 模板的实例化在编译阶段 必须能访问到源代码,而普通的函数或者类只需要有声明就可以了,放在symbol table中
#include <iostream>
#include <vector>
#include "List.h"
#include "List.cpp"
#include <string>
#include <exception>
#include "error.h"
#include <cassert>
using namespace std;

int div2(int i, int j){
	string s = "除数不能为0";
	Error *e = new MyExceptionZero(s);
	if(j == 0) throw e;     ///  throw e 抛出的e和catch中的类型一致就可以了  抛出异常由调用该函数的代码来解决异常
	else return i/j;
}


int main(){
	
	int i=1 , j;
	assert(i==1);
	while(cin >> i >> j){
		try{
			cout << div2(i, j) << endl;
		}catch(Error *e){   // 抛出指针是个坏主意,要求在对应的处理代码存在的任意地方存在指针所指向的对象
			e->showErrorMsg();
			delete e;
		}
		
	}
	return 0;
}

注意:1:除了以下类型转换外,异常的类型与catch说明符的类型必须完全匹配:

(1)   允许从非const到const的转换

(2)   允许从派生类类型到基类类型的转化

(3)   允许数组或者函数到指针的转换

2:在catch中可以通过调用throw重新抛出该异常给调用该函数进行处理

3:catch(…)可以捕获所有类型的异常。

4:auto_ptr类为动态分配的对象提供异常安全,该类在头文件memory中定义的。

 

二:命名空间

命名空间是为了防止名字冲突,定义为namespace 名字{}。命名空间的使用通常有3种方法:using 声明,重命名和using指示。

using声明:如using std::string; 

重命名:如 namespace s = std::string

using指示:如using namespace std

其中using声明是命名空间成员的局部别名一样,而一个命名空间可以有许多别名,所以别名以及原来的命名空间名字可以互换使用。Using指示它具有将命名空间成员提升到包含该命名空间本身。看下面的例子:

#include <iostream>
#include <string>
#include <vector>
//using namespace std;
using std::string;
namespace Exercise{
	int ivar = 0;
	double dvar = 0;
	const int limit = 100;
}

int ivar = 8;
position1
void manip(){

	position2
	double dvar = 3.141;
	int iobj = limit +1;
	++ivar;
	std::cout << "ivar: " << ivar << std::endl;
	++::ivar;
	std::cout << "::ivar: " << ::ivar <<std::endl;
}

(1)   在position1处使用using Exercise::ivar; usingExercise::dvar;using Exercise::limit; 则会出现ivar重定义。在position2处使用以上using声明,则会出现dval重定义,而++ivar为Exercise中的ivar,++::ivar为全局的ivar

(2)   在position1处使用using namespace Exercise; 则++ivar不知道使用的是Exercise中的ivar还是全局的ivar。在position2处使用usingnamespace Exercise的结果是一样的。

注意:1:命名空间可以是不连续的。

2:全局命名空间是隐含的,它没有名字,所以记号::member_name引用全局命名空间的成员。

3:未命名的命名空间的定义局部于特定的文件,从不跨越多个文本文件。

 

三:多重继承

多重继承遵循单集成的所有原则,唯一的不同点在于虚继承。虚继承为一种机制,类通过虚继承表明它希望共享其虚基类的状态。在虚派生中,由最底层派生类的构造函数初始化虚基类。如下面的图:

 

其初始化为:

上传失败....待传。。

注意:1:名字查找原则: 先在派生类自己的成员中进行查找,找不到再去基类中并行查找(不管该基类是直接基类还是派生基类,其优先级别是一样的)

四:并发—进程与线程

       进程是程序基于某一数据集合的一次运行活动。进程是操作系统进行资源分配和调度的一个独立单位。线程是进程的一个实体,CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。

       注意:1:线程的四种状态:新建状态;阻塞状态;运行状态和死亡状态。其中导致阻塞状态的原有有四种:sleep休眠;wait等待;等待IO操作以及即将使用已被锁的互斥锁的代码。生产者和消费者模型就是等待信号机制的典型,哲学家进餐问题是死锁的典型案例。死锁就是当一个线程等待另外一个线程,而另外一个线程又在等待其他的线程,最后一个线程又在等待第一个线程,那么就会形成一个相互等待的循环线程,这样就形成了死锁。解除哲学家进餐的死锁问题的的一个典型方法就是改变最后一个哲学家拿左右筷子的顺序就可以了。

参考文献:

1:

http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html

2:http://blog.chinaunix.net/uid-21411227-id-1826748.html

3:http://jingyan.baidu.com/article/624e74598efcc834e9ba5a66.html

 

抱歉!评论已关闭.