1.部分具体化
见代码:
#include<iostream>
using namespace std ;
template <class t ,class g >
class nums
{
t a ;
g b ;
public :
nums( t k , g m ) : a(k), b(m) {};
t count ( ) ;
};
template class nums<double,double > ;
template<class t1> class nums <t1, int > //部分具体化
{
int k ;
};
int main()
{
nums<double ,double>*ptr ;
// double c = l.count() ;
// cout << c <<endl;
return 0 ;
}
原来具体化的模板是
template<> class nums <int 》
{} ;
部分具体化只是在《》里多加了不完全具体化的成员
调用时有限度
部分具体化》模板
其他优先度已经在上文提到过
2.多元化模板的体现
2.1 template《class t1 》class thing 《 t1 , t1* ,t1*》
2.2模板的嵌套
// queuetp.h -- queue template with a nested class
#ifndef QUEUETP_H_
#define QUEUETP_H_
template <class Item>
class QueueTP
{
private:
enum {Q_SIZE = 10};
// Node is a nested class definition
class Node //嵌套
{
public:
Item item;
Node * next;
Node(const Item & i) : item(i), next(0) {}
};
Node * front; // pointer to front of Queue
Node * rear; // pointer to rear of Queue
int items; // current number of items in Queue
const int qsize; // maximum number of items in Queue
QueueTP(const QueueTP & q) : qsize(0) {}
QueueTP & operator=(const QueueTP & q) { return *this; }
public:
QueueTP(int qs = Q_SIZE);
~QueueTP();
bool isempty() const
{
return items == 0;
}
bool isfull() const
{
return items == qsize;
}
int queuecount() const
{
return items;
}
bool enqueue(const Item &item); // add item to end
bool dequeue(Item &item); // remove item from front
};
// QueueTP methods
template <class Item>
QueueTP<Item>::QueueTP(int qs) : qsize(qs)
{
front = rear = 0;
items = 0;
}
template <class Item>
QueueTP<Item>::~QueueTP()
{
Node * temp;
while (front != 0) // while queue is not yet empty
{
temp = front;
front = front->next;
delete temp;
}
}
// Add item to queue
template <class Item>
bool QueueTP<Item>::enqueue(const Item & item)
{
if (isfull())
return false;
Node * add = new Node(item); // create node
// on failure, new throws std::bad_alloc exception
items ++;
if (front == 0) // if queue is empty
front = add; // place item at front
else
rear->next = add; // else place at rear
rear = add;
return true;
}
// Place front item into item variable and remove from queue
template <class Item>
bool QueueTP<Item>::dequeue(Item & item)
{
if (front == 0)
return false;
item = front->item; // set item to first item in queue
items --;
Node * temp = front; // save location of first item
front = front->next; // reset front to next item
delete temp; // delete former first item
if (items == 0)
rear = 0;
return true;
}
#endif // QUEUETP_H_
看不下去了,有空再看吧
//主函数
//nested.cpp -- using a queue that has a nested class
#include <iostream>
#include <string>
#include "queuetp.h"
int main()
{
using std::string;
using std::cin;
using std::cout;
QueueTP<string> cs(5);
string temp;
while (!cs.isfull())
{
cout << "Please enter your name. You will be "
<< "served in the order of arrival.\n"
<< "name: ";
getline(cin, temp);
cs.enqueue(temp);
}
cout << "The queue is full. Processing begins!\n";
while (!cs.isempty())
{
cs.dequeue(temp);
cout << "Now processing " << temp << "...\n";
}
return 0;
}
3.将模板作为参数
声明 template《template《typename t》class thing 》
模板中的模板,这里把模板类thing作为参数
声明 crab《stack》abc ;//stack为类
4.在模板中使用友元函数
经过几个小时的努力,我终于搞了出来,见最后面
friend ostream& operator<< <T>(ostream& out, Complex& c); //注意观察,<T>的位置上
//这里用的输出友元注意t位置
友元尽量少用,在类模板里面,真的坑
5.非约束模板友元(只用类参)/约束模板友元(就是在类外有定义)
template <class T >
class Complex
{
T a=10 ;
public :
template <class a , class b>friend void show ( a& , b &) ;
};
就如字面意思一样,可以在类内定义非类模板的模板友元
下面是我的个人重测
我tm终于搞出来了,傻逼玩意
#include<iostream>
using namespace std ;
template <class oo>
void a () ; //友元具体化访问对应模板
template <class T >
class Complex
{
public :
T a ;
Complex (T kkk) : a(kkk) {} ;
template <class oo>friend void show ( oo& , Complex<T> &) ;
//模块定义
};
template <class oo ,class T > //模块声明
void show ( oo & zz, Complex<T>& gg ) //对模板类的调用
{
oo temp = zz*gg.a ;
cout<<temp <<endl;
}
int main()
{
double k = 10 ;
Complex<double> oop = Complex<double> (10.9) ;
show<double , double > (k,oop) ; //对模板友元函数的使用
//透他的
return 0 ;
}
6.友元成员函数和友元类
声明友元类
friend class A ;
class Complex
{
int a = 200 ;
double d = 100.23 ;
public :
friend class k ; //声明友元类
};
class k {
int c =20 ;
public :
void oo (Complex k ) ; //定义友元类的对象,使其可以直接被使用
};
void k::oo(Complex k)
{
cout<<k.a<<"ok"<<k.d<<endl; //可以直接访问私有成员
}
int main()
{
Complex opp ;
k oop ;
oop.oo(opp) ;
return 0 ;
}
引申
@1 互相友元类的使用
@2 共同友元函数(只能单独的函数友元)
7.嵌套类的访问权限
7.1 私有部分:包含类中可用,派生类中不可用,外部不可用
7.2 公有部分:包含类中可用,派生类中可用,外部可用
7.3 保护部分: 包含类中可用,派生类中可用,外部不可用
异常模块
类终于结束了,虽然留了一些难题,但是太过繁琐
1.先看几重简单的异常调用
#include<iostream>
#include <cstdlib>
#include<cfloat>
using namespace std ;
int main()
{
cout<<DBL_MAX<<endl; //浮点数最大值
cout<< "sfds"<<endl;
abort(); //缺点是调用后程序直接终止
return 0 ;
}
2.异常机制
核心表达式 try { … . .} ; catch {} ; throw ;
try语块内放可能出现异常的代码块,catch内放出现异常的处理办法,throw负责在函数中丢异常
简单的自定义和重载expection类
#include<iostream>
//#include<exception> //没有也能运作
using namespace std ;
struct myexception //这里定义了基本异常机制的运作方法
{
const char* what() const throw () //重定义what();
{return "my exception" ;}
};
double app (double a , int j )
{
if(j==0)
throw myexception() ; //引发异常
//throw "adsd" ; //为自定义版本
return a*j ;
}
int main()
{
int k = 0 , m = 6 ;
try
{
app(m,k) ;
}
catch(myexception&e) //接受异常
{
cout<<e.what() ; //显示异常原因
}
/** catch (const char * pd) //自定版本的catch
{
cout << pd <<endl ;
}
*/
return 0 ;
}
大体结构就是如此
3.异常机制的深入exception
函数在寻找处理代码的过程中退出:寻找处理代码的过程与函数调用链刚好相反。当异常被抛出时,首先搜索抛出该异常的函数。如果没有找到匹配的catch子句,终止该函数,并在调用该函数的函数中继续寻找。如果还是没有找到匹配的catch子句,这个新的函数也被终止,继续搜索调用它的函数。以此类推,沿着程序的执行路径逐层回退,直到找到适当类型的catch子句为止。如果最终还是没能找到任何匹配的catch子句,程序转到名为terminate的标准库函数。该函数的行为与系统有关,一般情况下,执行该函数将导致程序非正常退出。
如果一段程序没有try语句块且发生了异常,系统会调用terminate函数并终止当前程序的执行。
上述过程就是栈解退的一部分原理
标准异常类和其他异常类
我们只能以默认初始化的方式初始化exception、bad_alloc和bad_cast对象,不允许为这些对象提供初始值。其它异常类型的行为则恰恰相反:应该使用string对象或者C风格字符串初始化这些类型的对象,但是不允许使用默认初始化的方式。当创建此类对象时,必须提供初始值,该初始值含有错误相关的信息。
异常的种类
1.
(2)、exception头文件定义了最通常的异常类std::exception,它只报告异常的发生,不提供任何额外的信息。
exception总览
(3)、new头文件定义了bad_alloc异常类型。
(4)、type_info头文件定义了bad_cast异常类型。
当执行一个throw时,跟在throw后面的语句将不再被执行
有关于栈解退
@1 throw返回不是终止函数的返回,而是回推查找函数的调用对象返回
@2 正常的函数返回上一级的对象,而throw返回上一级try{}块的返回地址并释放中间函数的自动类对象(栈内存释放)
@3 如果找不到try块最终返回给main函数 //调用terminate
异常特性
@1 throw给的是副本而不是引用
@2 如果放在多重继承里面
if{ 1,2,3}
try
catch{3,2,1}
要把顺序调过来
@3 补充: catch(…){代码}能处理所有异常,通常放在最后面确保所有异常被抓到
@4 函数指针及该指针所指的函数必须具有一致的异常说明。
@5 如果一个虚函数承诺了它不会抛出异常,则后续派生出来的虚函数也必须做出同样的承诺;在这里插入代码片
noexcept异常说明
关键字noexcept放在函数的末尾表示该函数不会抛出异常
在成员函数中,noexcept说明符需要跟在const及引用限定符之后,而在final、override或虚函数的=0之前
另一种表示:如果函数被设计为是throw()的,则意味着该函数将不会抛出异常:void f(int) throw();
noexcept说明符接受一个可选的实参,该实参必须能转换为bool类型:如果实参是true,则函数不会抛出异常;如果实参是false,则函数可能抛出异常。
关于throw或者noexcept中参数的再说明,里面如果是空则表示不会返回异常;如果为…则表示会返回任意异常;如果为特定异常类型则表示只会返回声明的异常
违反异常说明:
如果定义了noexcept()或者throw() 但是又在函数块里面使用了throw()语句,系统编译通过但是在运行时会直接调用terminate
进阶代码
#include<iostream>
#include<exception>
using namespace std;
const string egg="I not like this number,so I decided to refuse it.";
class non_44_error: public logic_error{
public:
explicit non_44_error(const string &s=egg):logic_error(s){}
}; //和上面的一样,目的是重定义logic_error为新名称并赋予新的异常原因
//下面传参的时候就不用再次给string值了/char*值了
int main(){
int input;
while(1){
try{
cout<<"Please type in a number between 1 and 100."<<endl;
cin>>input;
if(!cin.good()){
cin.clear();
cin.ignore();
throw invalid_argument("The input should be a number!");
}
if(input>=100)
throw length_error("The input should be less than 100!");
if(input<0)
throw out_of_range("The input should be Non-negative number!");
if(input==44)
throw non_44_error(); //重定义异常的使用
cout<<"Your input is "<<input<<". there isn't error\n";
} catch(invalid_argument e){
cout<<"*********************************"<<endl;
cout<<"There is an invalid argument error occured"<<endl;
cout<<"info:"<<e.what()<<endl;
cout<<"*********************************"<<endl;
} catch(length_error e){
cout<<"*********************************"<<endl;
cout<<"There is a length error occured"<<endl;
cout<<"info:"<<e.what()<<endl;
cout<<"*********************************"<<endl;
} catch(out_of_range e){
cout<<"*********************************"<<endl;
cout<<"There is an out of range error occured"<<endl;
cout<<"info:"<<e.what()<<endl;
cout<<"*********************************"<<endl;
} catch(non_44_error e){
cout<<"*********************************"<<endl;
cout<<"There is an error occured"<<endl;
cout<<"info:"<<e.what()<<endl;
cout<<"*********************************"<<endl;
} catch(exception e){
cout<<"*********************************"<<endl;
cout<<"There is an undefined error occured"<<endl;
cout<<"info:"<<e.what()<<endl;
cout<<"*********************************"<<endl;
}
cout<<endl;
}
return 0;
}
输出:
Please type in a number between 1 and 99.
99
Your input is 99. there isn't any error
Please type in a number between 1 and 99.
1000
*********************************
There is a length error occured
info:The input should be less than 100!
*********************************
Please type in a number between 1 and 99.
-1
*********************************
There is an out of range error occured
info:The input should be Non-negative number!
*********************************
Please type in a number between 1 and 99.
d
*********************************
There is an invalid argument error occured
info:The input should be a number!
*********************************
Please type in a number between 1 and 99.
44
*********************************
There is an error occured
info:I don't like this number,so I decide to refuse it.
*********************************
exception的详细成员介绍和代码来源
除了自定义重命名类之外,其他调用都很简单
感觉除了标准异常外,其他异常就是扔给我们当模板玩玩的,只是省去了我们定义的过程,因为我们可以自己定义what内容
重新找了一张更具体的图
以上所有没有子类的异常全部为标准异常
也就是不带参数在直接catch() 的异常
看一个bad_expection的例子
#include <iostream>
#include <exception>
#include <stdexcept>
void my_unexp() { throw; }
void test() throw(std::bad_exception) //声明只会返回这种异常
{
throw std::runtime_error("test"); //违背
}
int main()
{
std::set_unexpected(my_unexp); //设置意外异常的处理方式
try {
test();
} catch(const std::bad_exception& e)
{
std::cout << "Caught " << e.what() << '\n';
}
}
查了好久不知道为毛要设置,但是不设置就是运行时错误
//异常具体使用法则见网址,到此为止
笔记中找到了
见下一块
4.异常为何经常会迷失方向
当出现意外异常没有被捕获时,系统会默认调用terminate函数,terminate函数又会默认调用abort函数来终止程序
所以
可以自定义函数A (如上一篇代码所做的那样)//void类型无参,必须包含throw等操作
然后把set_terminate(A);放在开始让函数出现意外时默认调用A
以上的主要目的是修改未捕获终止调用函数
当函数出现意外异常(如与声明冲突)默认调用unexpected函数然后如果为定义后又会调用默认调用terminate函数
调用set_unexpect(A) ;
以上的主要目的是修改意外异常终止调用函数
都是重throw异常使其被catch
应该是由于优先度的原因所以要自己重设来捕获
5.补充一小点:关于new时的异常传递
小技巧,如果在try中捕获了带new的异常,可以全部回扔,然后在catch语块中将内存释放,然后二次回扔,在更高一介的地方进行异常的处理
关于类继承中的异常使用上文提到过,见第二篇代码中的调用方式
const char* s = “fdsdf” ;
class newexp : public runtime_error ;
{
newexp(const char* =s ) : runtime_error(s) ;
};
//调用时直接用新名字就好了
异常结