C++ Primer Plus 15章

//友元类
class Tv
{
public:
friend class Remote; //Remote可以访问Tv的私有成员
//...
private:
//...
}


class Remote
{
private:
//...
public:
Remote(int m = Tv::TV):mode(m);
bool volUp(Tv &t){return t.volup;}
//...
}


tips:
void onoff(){state = (state == On ) ? off : on;} //是否off就设置为on 是on就设置为off
void onoff(){state ^= 1;} //^ 按位异或 


//友元成员函数--只让友元类的某个成员函数有友元权限
class Tv
{
friend void Remote::set_chan(Tv &t, int c);
//...
};
这样做必须要让C++知道Remote与set_chan是什么东西,为此,需要如下操作
class Tv; //只是一个声明,需要这句是因为Remote中用到了Tv
class Remote{...}; //声明,并声明类方法
class Tv{...}; //声明,并声明类方法,此时,Tv就知道了Remote与set_chan是什么意思了
但是,Remote中有一句 void onfoff(Tv &t){t.onoff();} C++并不知道Tv 的 onoff方法,所以
将Remote的成员函数具体实现放在Tv具体实现后,通过指定 inline ,依然可以让其成为内联方法


//其他友元关系
1 相互友元
class Tv
{
friend class Remote;
public:
void buzz(Remote & r);
//...
}
class Remote
{
friend class Tv;
public:
void Bool volup(Tv &t);
}
这里要注意方法必须让各个类提前知道是什么含义


2 共同的友元
class Analyzer; //提前声明
class Probe
{
friend void sync(Analyzer & a, const Probe    & p);
friend void sync(Probe    & a, const Analyzer & p);
//...
};
class Analyzer
{
friend void sync(Analyzer & a, const Probe    & p);
friend void sync(Probe    & a, const Analyzer & p);
//...
};
//具体实现。。。。这两个函数同时为 Analyzer 与 Probe 的友元函数
inline void sync(Analyzer & a, const Probe    & p)
{
//...
}
inline void sync(Probe    & a, const Analyzer & p);
{
//...
}


//嵌套类
ps:结构是一种默认情况下为公有的类
嵌套类,结构,枚举的作用域特征
声明位置 包含它的是否可以使用它 包含它的类的派生类是否可以使用它 外部世界是否可以使用
私有部分   是
保护部分   是
公有部分           是 是,通过类限定符 ::
ex:
class Team
{
public:
class Coach{...};
//...
}
Team::Coach coach; //声明了一个Team的嵌套类Coach


//模板中的嵌套
没有影响,直接使用
template <typename T>
class QueueTP
{
private:
//...
class Node{
public:
T t; //直接使用即可
Node *next;
Node(const Item &i):item(i),next(0){}
};
//...
}


//异常机制
异常提供了将控制权从程序的一个部分传递到另一部分的途径
异常的处理有3个部分
1 引发异常
2 捕获有处理程序的异常
3 使用try块
ex:
double hmean(double a, double b)
{
if(a == -b)
thorw "bad hmaeand() arguments: a = -b not allowed"; //异常发生时,沿函数调用序列后退,找到包含try块的函数,并执行 catch
return a/(a+b);
}


int main()
{
//...
try{
z = hmean(3, -3);
}catch(const char *s)
{
cout << s << endl;
}
//...
}


有异常而没有相应处理时,程序将会调用abort()函数 (向错误流 cerr 发送abnornal program termination消息并终止程序)


//将对象用作异常类型
calss Bad_hmean
{
//...
};
double hmeans(double a, double b) throw (bad_hmean, bad_gmeans);
try{
//...
double z = hmeans(1,-1);
}catch(bad_heman &bg){
//...
}catch(bad_gmeans &bg){
//...
}
//RTTI Run Time Type Identification 运行阶段类型识别
C++有3个支持RTTI的元素
1 如果可能的话,dynamic_cast 操作符将使用一个指向基类的指针来生成一个指向派生类的指针;否则,该操作符返回0-空指针。
2 typeid操作符返回一个指出对象类型的值。
3 type_info结构存储了有关特定类型的信息
NOTE:RTTI只适用于包含虚函数的类,因为只有对于这种层次结构,才应该将子类对象赋给父类指针(否则,会直接报错)


RTTI的三个元素详细介绍:
1 dynamic_cast操作符
它不能回答 "指针指向的是那种对象" 但是能够回答 "是否可以安全的将对象的地址赋给特定类型的指针"
class A{};
class B : public A{};
class C : public B{};
A *a = new A;
B *b = new B;
C *c = new C;
C *pc = (C *)a; //编译可行,但很危险,因为将父类对象赋给了子类指针,子类指针就可以使用子类的一些派生方法和对象了,而这些子类的新对象是随机初始化的
如果 a 有 virtual 方法,那么可以使用RTTI(这是因为,只有a有 virtual 方法时,这样的赋值才会有意义<-多态行为)
dynamic_type<Type *>(pt);
如果可以。则返回对象的地址,否则,返回一个NULL指针
成功是指:(*pt对象为Type或者是从Type直接或间接派生而来的)

也可以用于引用,不过失败时,是抛出异常 bad_cast

2 typeid 操作符与 type_infor 类
typeid操作符确定两个对象是否为同种类型,可以接受两种参数
1 类名
2 结果为对象的表达式
typid操作符返回一个对type_info对象的引用,其中,type_info是在头文件
ex:
typeid(C) == typeid(*c); //==两边都返回的type_info类,其重载了 operator==() 与 operator!=()
相等为 true  否则为 false, 如果c是一个空指针,则抛出 bad_typeid 异常
type_infor在不同的编译器中不同,但一般的有一个.name()成员函数,返回一个字符串,通常是类的名字
ex:
cout << "Now type" << typeid(*c).name() << "endl";


3 (加入RTTI将会使得程序效率变低)




//类型转换操作符
C++有4中类型转换操作符
Class A{};; 
Class B : Public A{};
1 dynamic_cast;
使得在有 virtual 类派生层次中,可以安全的进行向上的转换 (子类赋给父类)
B *b = new B;
A *a = dynamic_cast<A*>(b);
PS:在运行时检查
2 const_cast;
只用与一种用途的类型转换:即改变值为 const 或 volatile ,只能这两种属性,其他的改变(如子类赋给父类)等将会出错
A a;
cosnt A * pca = &a;
A *pa = const_cast<A *>(pca); //这样pa就是一个可以修改a对象的指针了
const_cast 可以修改一个指向值的指针,但是修改 const 的值结果是不确定的。
ex:
const int consta = 1;
int * a = const_cast<int*>(&consta);
*a = 2;
则  *a d的值为 2 ; consta 的值为 1 , 即consta的值没有被修改,int * a = const_cast<int*>(&consta);,编译器为 *a 生成了一个临时变量
3 static_cast;
static_cast <type_name> (expression)
仅当type_name可以被隐式转化为expression所属的类型或expression可以隐式转换为type_name所属类型时才允许转换
简单的可以说是有关系的就允许
A a;
B b;
A *pa = static_cast <A *> (&b);
B *pb = static_cast <B *> (&a);
class C{};
C *c = static_cast <C *> (&b); //错误
PS:VS在编写时就可以检查出来
4 reinterpret_cast;
非常规转换
struct data{short a; short b};
long value = 0xA224B118;
dat *pd = reinterpret_cast<dat *>(&value);
pd-> a 将会使value的前两个字节,这里自然会存在大小端的问题
一些限制:
不能将指针转换为不能存储指针大小的类型
不能将函数指针与数据指针互相转换





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值