1.友元类,就在类中声明个friend class className;就行了。
这个是对所有成员有效的。
class A
{
public:
A(int a){ _a = a; }
friend class B;
private:
int _a;
};
class B
{
public:
int get(A a){ return a._a; }
protected:
private:
};
B类并不需要向前声明,因为已经指出,这是一个了类了,但是友元成员函数就需要了。
2.友元成员函数
class A;
class B
{
public:
int get(A& a);
protected:
private:
};
class A
{
public:
A(int a){ _a = a; }
friend int B::get(A& a);
private:
int _a;
};
必须要注意的是,在声明友元类函数的时候,必须已经知道了那个类的细节。所以那个类需要已经声明。
因为友元类函数是在别的类中声明了一次,所以在自己的类中必须是public的,否则看不到吧。
3.友元可以互相指向,即按上面的,B是A的友元类,同时A也可以为B的友元类。
但是要注意,使用对方的属性或者函数的时候,必须知道类的细节了。
4.在一个类中声明的类叫做嵌套类。
5.异常类型可以是字符串,或者其他类型。
执行throw相当于return,但是并不是返回调用的程序,而是沿着调用序列一直后退,知道被catch住。
6.异常规范,在函数原型和定义中的那个throw()
void a()throw("xxxxxx");
void b()throw();
throw()就是异常规范了。C++11已经摒弃了。
但是C++11新增了一个noexcept关键字。
void c()noexcept;
这是向编译器说明,这里不会引发异常,相当于程序员对编译器做出的一种承诺。
为了方便编译器进行优化。
7.引发异常后,对于中间调用放在栈中的自动对象,其析构函数将不会被调用。
但是通过下面程序。
Test.h
class A
{
public:
A(int a){ _a = a; cout << "a:" << _a << endl; }
~A(){ cout << "A:" << _a << endl; }
friend int B::get(A& a);
private:
int _a;
};
main.cpp
#include
#include "Test.h"
#include
using namespace std;
using namespace FableGame;
void f1()
{
A a(1);
throw a;
}
void f2()
{
A a(2);
f1();
}
void f3()
{
A a(3);
f2();
}
int main(int argc, const char * argv[])
{
try
{
f3();
}
catch (A& a)
{
cout << "main" << endl;//在这里断点
}
return 0;
}
在抛出异常后,直接进入catch模块了。而且通过打印输出,可以发现栈解退了,看VS的调用堆栈,f1,f2,f3的栈信息也被清空了。
所以说,程序进行栈解退,以回到捕捉异常的地方时,将释放栈中的自动存储变量。如果是类对象,将调用对象的析构函数。
但是对于new出来的对象,后面的delete可能没有执行到,导致内存泄漏。
8.捕捉到的异常,都是对象的副本,因为异常本身已经在栈解退时析构了。
使用引用,是因为抛出的异常,可能是有多重的继承,通过捕捉基类,可以捉到这条继承链的所有异常。
9.在程序没有捕获异常终止的时候,将会调用abort()函数。但是我们可以替换这个被调用的函数。
#include
void myQuit()
{
cout << "Fable Game" << endl;
exit(123456);
}
set_terminate(myQuit);
只要在程序开头的地方执行这些就可以了。
10.RTTI运行阶段类型识别(Runtime Type Identification)
dynamic_cast运算符,尝试类型转换,如果不行就返回空指针。
typeid运算符使得能够确定两个对象是否为相同类型。typeid运算符返回一个type_info对象。
typeid运算符可以接受类名或者对象。
A a(123);
if (typeid(a) == typeid(A))
{
cout << typeid(a).name() << endl;//输出类名 class FableGame::A
}
name()输出完整的类名。
11.类型转换运算符
dynamic_cast,用得最多的了,如果能转就返回地址,如果不能就返回空指针。
const_cast ,可以转换const和非const
void change(const int* p, int value)
{
int *p2 = const_cast(p);
*p2 = value;
}
int main(int argc, const char * argv[])
{
int a = 123;
const int b = 456;
cout << a << b << endl;
change(&a, 1);
change(&b, 2);
cout << a << b << endl;
return 0;
}
const_cast只能改变指针,但是如果对象本身就是const的话,里面的值是无法更改的。
结果输出是:
123456
1456
static_cast,编译器可以隐式转换的,这个都可以完成。只在编译的时候确保安全性。
reinterpret_cast,通常适合于依赖具体实现的底层编程,不可移植的。