混沌 IN C++::is-a的误解

rel="File-List" href="file:///C:%5CUsers%5CADMINI%7E1%5CAppData%5CLocal%5CTemp%5Cmsohtml1%5C08%5Cclip_filelist.xml">

度:

 

rel="File-List" href="file:///C:%5CUsers%5CADMINI%7E1%5CAppData%5CLocal%5CTemp%5Cmsohtml1%5C10%5Cclip_filelist.xml">

知识回顾:

    两个类在什么情况下是“is-a”关系?

   

class B

{

public:

void foo();

};

 

class D: public B

{};

 

D 公有继承自 B,那么DB就是“is-a”关系,且D is-a B。下面的这个代码能够很好的体现出这种关系。

 

D d;

d.foo();

 

foo其实是B::foo。那么用对象d来调用这个成员函数时,由于d的类型Dis-a B,那么B::foothis指针就由对象d的地址转换而来。d.foo()这样的调用就顺理成章地通过编译。

 

问题:

    现在项目经理给你提出了一个要求叫你提供一个容器类,实现保存任意类的对象指针和该类的无返回值无参数非cv-qualified的成员函数地址,并提供一个接口以调用里面所有的成员函数。你该怎么实现呢?只要学过模板,这个问题应该不难,稍加片刻就能得出答案。

 

rel="File-List" href="file:///C:%5CUsers%5CADMINI%7E1%5CAppData%5CLocal%5CTemp%5Cmsohtml1%5C02%5Cclip_filelist.xml">

class any_mf

{

public:

virtual ~any_mf();

virtual void fire() = 0;

};

 

template<typename T>

class mf_holder: public any_mf

{

public:

mf_holder(T& object, void(T::*mf)())

:object_(object), mf_(mf)

{}

 

virtual void fire()      //重写any_mf::fire

{

(object_.*mf_)();

}

private:

T& object_;

void (T::*mf_)();

};

 

class container

{

public:

//其他函数在此省略

template<typename T>

void save(T& object, void(T::*mf)())

{

any_mf * amf = new mf_holder<T>(object, mf);

try

{

cont_.push_back(amf);

}

catch(std::bad_alloc&)

{

delete amf;

}

}

 

void fire()

{

std::for_each(cont_.begin(), cont_.end(), *this);

}

 

void operator()(any_mf* amf)

{

if(amf)

amf->fire();

}

private:

std::vector<any_mf*> cont_;

};

 

思考一下container::save会有什么问题?



答案:

    这个container对下面的代码无法通过编译。这里使用上面代码中的class Bclass D

 

D d;

container cont;

cont.save(d, &D::foo);

 

    因为无法精确匹配save的模板参数T,从第一个参数推导,可得到Tclass D,而从第二个参数推导,T则为class B。为什么会导致这样呢?因为D中并没有定义名为foo的成员函数。普遍认为D继承自B,那么foo也应该被D继承下来,的确是这样,因为名字查找规则允许D::foo这样的写法,但是foo在这里并不是D的,而是B。函数模板参数的推导是精确匹配的,在推导过程中,是不能将void (B::*)()转换为void(D::*)()的。那么怎么解决这个问题呢?显式指定模板参数。

 

cont.save<D>(d, &D::foo);

 

    当然,这种方法太落后,我们可以让再加一个模板参数,运用点模板的技巧来解决此问题。

 

template<typename T, typename Abstract>

void save(T& object, void (Abstract::*mf)())

{

    if(is_a<T, Abstract>::value)  

       cont_.push_back(new mf_holder<T>(object, mf));

    //这里用该用类的特化。而不是简单的if,在此省略具体做法

}

 

这是一个很简单的问题,但又很容易被忽略掉。

 

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值