时间:2018/11/27
地点:元征总部
天气:多云
笔记
1.模板特殊化:当模板中的pattern有确定的类型时,模板有一个具体的实现。
(1)例如假设我们的类模板pair包含一个取模计算的函数,而我们希望这个函数只有当对象中存储的数据为整型的时候才
能工作,其他的时候这个函数总是返回0。例子:
template <class T> class pair {
T value1, value2;
public:
pair (T first, T second){
value1=first;
value2=second;
}
T module () {return 0;}
};
template <>
class pair <int>{
//模板特殊化定义
int value1, value2;
public:
pair (int first, int second){
value1=first;
value2=second;
}
int module ();
};
int pair<int>::module() {
//函数重载
return value1%value2;
}
int main () {
pair <int> myints (100,75);
pair <float> myfloats (100.0,75.0);
cout << myints.module() << '\n';
cout << myfloats.module() << '\n';
return 0;
}
输出:
25
0
2.模板的参数1:除了模板参数前面跟关键字class或typename表示一个通用类型外,函数模板和类模板还可以包含其他
不适代表一个类型的参数,例如代表一个常数,这些通常是基本数据类型的。例如,下面的例子定义了一个用来存储数组的
类模板:
template <class T, int N>
class array{
T memblock[N];
public:
void setmember(int x,T value);
T getmember(int x);
};
template<class T, int N>
void array<T,N>::setmember(int x,T value)
{
memblock[x] = value;
}
template <class T, int N>
T array<T,N>::getmember(int x){
return memblock[x];
}
int main()
{
array <int,5> myints;
array <float,5> myfloats;
myints.setmember(0,100);
myfloats.setmember(3,3.1416);
cout << myints.getmember(0) << '\n';
cout << myfloats.getmember(3) << '\n';
return 0;
}
输出:
100
3.1416
3.模板的参数2:也可以为模板参数设置默认值,就像为函数参数设置默认值一样
下面是一些模板定义的例子:
template <class T> // 最常用的:一个class 参数。
template <class T, class U> // 两个class 参数。
template <class T, int N> // 一个class 和一个整数。
template <class T = char> // 有一个默认值。
template <int Tfunc (int)> // 参数为一个函数。
4.模板与多文件工程:不同于通常的接口和实现,它们通常分别被放在在.h和.cpp文件中;而模板的接口和实现只能放在同
一个文件(模板文件template file)中。
5.名空间:将一组全局范围有效的类、对象或函数组织到一个名字下面
(1)定义:namespace identifier{ namespace-body }
(2)使用:using namespace identifier;
(3)范围:只在一个语句块中有效(即用{}包括的代码)
(4)别名:namespace new_name = current_name;
6.出错处理1:
try{
//code to be tried
throw exception;
}
catch(type exception)
{
//code to be executed in case of exception
}
说明:
(1)try语句块中的代码被正常执行。如果有例外发生,代码必须使用关键字throw和一个参数来扔出一个例外。这个参数
可以是任何有效的数据类型,它的类型反应了例外的特征。
(2)如果有例外发生,也就是说在try语句块中有一个throw指令被执行了,则catch语句块会被执行,用来接收throw传来
的例外参数。
(3)例子1:
char myarray[10];
try{
for(int n=0;n<=10;n++)
{
if(n>9)throw "Out of range";
myarray[n] = 'z';
}
}
catch (char *str){
cout << "Exception: " << str << endl;
}
7.出错处理2:可以用"..."来捕获所有例外
try{
//code here
}
catch(...){
//code to be executed in case of exception
}
8.出错处理3:如果由于没有对应的类型,一个例外没有被任何catch语句捕获,特殊函数terminate将被调用。
这个函数通常已被定义,以便立即结束当前的进程,并显示一个“非正常结束”("Abnormal termination")的出错信息。
9.出错处理4:标准例外
一些C++标准语言库中的函数也会扔出一些例外,这些例外扔出的参数都是std::exception引申出的子类类型。下面的例子
中,一个类型为bad_typeid的例外(exception的引申类),在要求类型信息的对象为一个空指针的时候被捕获:
#include <iostream>
#include <exception>
#include <typeinfo>
using namespace std;
class A{virtual void f(){};};
int main()
{
try
{
A *a = NULL;
typeid(*a);
}
catch(exception& e)
{
cout << "Exception: " << e.what();
}
return 0;
}
10.ANSI-C++标准定义了4种新的类型转换操作符:reinterpret_cast,static_cast,dynamic_cast和const_cast。
所有这些操作符都是同样的使用格式:
(1)reinterpret_cast <new_type> (expression):
reinterpret_cast可以讲一个好自在张伟任意其他类型的指针,也可以用来讲一个指针转换为一个整型,反之亦然。
这个操作符可以在互不相关的类之间进行指针转换操作的结果是简单的讲一个指针的二进制数据复制到另一个指针。对指针
指向的内容不做任何检查或转换。
如果这种复制发生在一个指针到一个整数之间,则对其内容的解释取决于不同的系统,因此任何实现都是不可移植的。一个
指针如果被转换为一个能够完全存储它的足够大的整数中,则可以再被转换回来成为指针。
例如:
class A{};
class B{};
A *a = new A;
B *b = reinterpret_cast<B*>(a);
reinterpret_cast对所有指针的处理和传统的类型转换符所做的一模一样。
(2)dynamic_cast <new_type> (expression):
dynamic_cast完全被用来进行指针的操作。它可以用来进入任何可以隐含进行的转换操作以及它们被用于多态类情况下的方
向操作。然而与static_cast不同的是,dynamic_cast会检查后一种情况的操作是否合法,也就是说它会检查类型转换操作
是否会返回一个被要求类型的有效的完整的对象。
这种检查是在程序运行过程中进行的。如果被黄钻和的指针所指向的对象不是一个被要求类型的有效完整的对象,返回值将
会是一个空指针NULL 。
class Base { virtual dummy(){}; };
class Derived : public Base { };
Base* b1 = new Derived;
Base* b2 = new Base;
Derived* d1 = dynamic_cast(b1); // succeeds
Derived* d2 = dynamic_cast(b2); // fails: returns NULL
如果类型转换被用在引用(reference)类型上,而这个转换不可能进行的话,一个bad_cast 类型的例外(exception)将会被
抛出:
class Base { virtual dummy(){}; };
class Derived : public Base { };
Base* b1 = new Derived;
Base* b2 = new Base;
Derived d1 = dynamic_cast(b1); // succeeds
Derived d2 = dynamic_cast(b2); // fails: exception thrown
(3)static_cast <new_type> (expression):
static_cast可以执行所有能够隐含执行的类型转换,以及它们的反向操作(即使这种方向操作是不允许隐含执行的)。
用于类的指针,也就是说,它允许将一个引申类的指针转换为其基类类型(这是可以被隐含执行的有效转换),同时也允许
进行相反的转换:将一个基类转换为一个引申类类型。
在后面一种情况中,不会检查被转换的基类是否真正完全是目标类型的。例如下面的代码是合法的:
class Base{};
class Derived:public Base{};
Base *a = new Base;
Derived *b = static_cast(a);
static_cast 除了能够对类指针进行操作,还可以被用来进行类中明确定义的转换,以及对基本类型的标准转换:
double d = 3.14159265;
int i = staitc_cast<int>(d);
(4)const_cast <new_type> (expression)
这种类型转换对常量const 进行设置或取消操作:
class C {};
const C * a = new C;
C * b = const_cast<C*> (a);
其他3种cast 操作符都不可以修改一个对象的常量属性(constness)。11.ANSI-C++ 还定义了一个新的操作符叫做 typeid ,它检查一个表达式的类型:typeid (expression)
这个操作符返回一个类型为type_info的常量对象指针,这种类型定义在标准头函数中。这种返回值可以用操作符 == 和 !=
来互相进行比较,也可以用来通过name()函数获得一个描述数据类型或类名称的字符串,例如:
#include <iostream>
#include <typeinfo>
using namespace std;
class CDummy{};
int main()
{
CDummy* a,b;
if(typeid(a) != typeid(b)){
cout << "a and b are of different types:\n";
}
return 0;
}