《C++ Primer Plus(第六版)》(31)(第十五章 友元、异常和其他 笔记)

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 <iostream>     
#include "Test.h"
#include <string> 
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 <exception>
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<int*>(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,通常适合于依赖具体实现的底层编程,不可移植的。

















posted @ 2016-12-29 21:13  肥宝游戏  阅读(240)  评论(0编辑  收藏  举报