The clone pattern
来源:互联网 发布:股票指标分析软件 编辑:程序博客网 时间:2024/06/05 23:01
The clone pattern
Jan 27, 2010 at 11:31pm
jsmith (5804)
In order to copy an object, you have to know at compile time the
object's type, because the type is the "name" of the copy constructor:
I know at compile time that s has type "std::string" because it says
so in the parameter list. But what if type of s was a base class?
That doesn't quite work, because I can call copy_me() with a derived
class instance, in which case the function would want to instantiate
a Derived object, not a Base object. But at compile time there is
simply no way for me to know that. Indeed, I could even call copy_me()
with Base instances in one place, Derived in another, and something
else (derived from Base or Derived) in a third.
How can this problem be solved?
The clone pattern was implemented for exactly this reason. The
clone pattern looks like this:
And now, copy_me looks like:
And I've successfully invoked Base's copy constructor if the
"real type" of b is Base, and Derived's copy constructor if the
"real type" of b is Derived.
It is worth mentioning here that this technique exploits the fact that
the compiler does not consider the return type of the function when
determining whether or not a derived class virtual method has overridden
a base class one with the same name.
object's type, because the type is the "name" of the copy constructor:
1234
void copy_me( const std::string& s ) { std::string s_copy( s ); std::string* p_s_copy = new std::string( s );}
I know at compile time that s has type "std::string" because it says
so in the parameter list. But what if type of s was a base class?
123456
class Base {};class Derived : public Base {};void copy_me( const Base& b ) { Base b_copy( b ); // ????}
That doesn't quite work, because I can call copy_me() with a derived
class instance, in which case the function would want to instantiate
a Derived object, not a Base object. But at compile time there is
simply no way for me to know that. Indeed, I could even call copy_me()
with Base instances in one place, Derived in another, and something
else (derived from Base or Derived) in a third.
How can this problem be solved?
The clone pattern was implemented for exactly this reason. The
clone pattern looks like this:
12345678910111213141516171819
// Depending upon your needs, you might not require a base class// clonable concept. It would only be needed if you need to store// clonable objects polymorphically.struct clonable { virtual ~clonable() {} virtual clonable* clone() const = 0;};class Base : public clonable { public: virtual Base* clone() const { return new Base( *this ); }};class Derived : public Base { public: virtual Derived* clone() const { return new Derived( *this ); }};
And now, copy_me looks like:
1234
void copy_me( const Base& b ) { Base* clone = b.clone(); // delete clone; };
And I've successfully invoked Base's copy constructor if the
"real type" of b is Base, and Derived's copy constructor if the
"real type" of b is Derived.
It is worth mentioning here that this technique exploits the fact that
the compiler does not consider the return type of the function when
determining whether or not a derived class virtual method has overridden
a base class one with the same name.
Feb 28, 2010 at 3:21am
hamsterman (4538)
In order to copy an object, you have to know at compile time the
object's type
I think you can solve this problem in another way. If you know object's type at runtime you can copy it. For example:
object's type
I think you can solve this problem in another way. If you know object's type at runtime you can copy it. For example:
12345678910111213141516171819202122
struct Base{ virtual int Type() const{ return 0; }};struct Derived: Base{ virtual int Type() const{ return 1; }};void copy_me(const Base& b){ Base* object = 0; switch(b.Type()){ case 0://Base object = new Base(b); break; case 1://Derived object = new Derived((Derived&)b); break; }
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;);}
};
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;
};
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());
}
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不如虚指针快。
- The clone pattern
- Clone in the PHP
- The second pattern-------Observer Pattern
- The third pattern------Decorator Pattern
- The forth pattern-----Factory Pattern
- The fifth pattern------Singleton Pattern
- The sixth pattern------------Command Pattern
- The tenth pattern---------------State Pattern
- The eleventh pattern------Proxy Pattern
- The twelfth pattern------Compound Pattern
- The seventh pattern --------The Adapter Pattern and Facade Pattern
- [抄书]The Layers pattern
- The Factory Design Pattern
- PDP-The Factory Pattern
- [PDP] - The Adapter Pattern
- The Facade Pattern
- The Adapter Pattern
- The Bridge Pattern
- Iframe背景色设置
- 黑马程序员 银行业务调度系统
- c++ 中的static
- linux device suspend/resume test
- linux的核心
- The clone pattern
- 开始学习JAVA
- 远程脑服务系统构架
- 实验四十八微软应用程序虚拟化之三APP-V 5.1 Client部署和通过组策略自定义配置
- OS的核心
- 使用Visual Studio 开发、调试WCF入门-1-零代码开发、调试WCF服务器端。HelloWorld
- 抽象工厂模式
- Hibernate SQL 方言(hibernate.dialect)
- 使用Visual Studio 开发、调试WCF入门-2-无脑建立客户端