c++ 中虚拟克隆(virtual clone)

又看了一遍more effective c++, 注意到以前没怎么记住的virtual clone技术。

class Base {
public:
    
virtual Base* clone();
};

class Impl1 {
public:
    
virtual Impl1* clone(){return new Impl1(*this);}
};

class Impl2 {
public:
    
virtual Impl2* clone(){return new Impl2(*this;);}
};

初一看没什么特别的。clone只是调用了拷贝构造函数。确实,在没有类层次结构的情况下,一个这样的clone()并不会比直接写拷贝构造有用。

奇特的是, 这里3个clone的返回类型是不一样的,但是编译器依然认为后两者是对基类clone()的实现!事实上不只是指针,返回引用也可以达到同样的效果。(题外话,虽然这里clone的语义显然只能返回指针,没人会返回一个局部对象的引用)。

 

 正是由于这种特性,使得以下实现成为可能:

 

class Container {
public:
    Container(
const Container& c) {
        
for (List<Base*>::const_iterator it = c.items.begin(); it != c.items.end(); ++it) {
            items.push_back(
*it->clone());
    }
private:
    List
<Base*> items;
};

 这样,每次调用的都是动态类型的clone函数,尽管他们返回的是不同类型,依然具备多态特性。(再扯一句题外话,这里容器里保存指针是很危险的,显然通过拷贝构造的Container对象必须在析构函数里负责delete他们。但是,倘若该Container有其他方式,比如某个push函数,保存指针,那么, container是否有权在析构是删除这些指针就是个问题了。 所以,当容器中不得不使用指针时,还是是最好用shared_ptr或auto_ptr)

 

 想起昨天在项目代码中也看到了一个clone()的实现。一看竟然没用到虚拟克隆。于是又这样的代码:

 

Address::ptr
Address::create(
const sockaddr *name, socklen_t nameLen)
{
    MORDOR_ASSERT(name);
    Address::ptr result;
    
switch (name->sa_family) {
        
case AF_INET:
            result.reset(
new IPv4Address());
            MORDOR_ASSERT(nameLen 
<= result->nameLen());
            memcpy(result
->name(), name, nameLen);
            
break;
        
case AF_INET6:
            result.reset(
new IPv6Address());
            MORDOR_ASSERT(nameLen 
<= result->nameLen());
            memcpy(result
->name(), name, nameLen);
            
break;
        
default:
            result.reset(
new UnknownAddress(name->sa_family));
            MORDOR_ASSERT(nameLen 
<= result->nameLen());
            memcpy(result
->name(), name, nameLen);
            
break;
    }
    
return result;
}

Address::ptr
Address::clone()
{
    
return create(name(), nameLen());
}

 

 显然,不足是基类的实现需要涉及所有存在的子类。而且,较真一点,时间效率也是问题。毕竟if else或者switch不如虚指针快。

 

 

 

 

posted on 2011-06-01 11:33  freestyleking  阅读(3611)  评论(0编辑  收藏  举报

导航