【发布时间】:2012-11-09 14:30:45
【问题描述】:
比较这两种技术的优点/缺点是什么?更重要的是:为什么以及何时应该使用一个而不是另一个?这只是个人品味/偏好的问题吗?
尽我所能,我还没有找到其他明确解决我问题的帖子。在有关多态性和/或类型擦除的实际使用的许多问题中,以下似乎是最接近的,或者看起来如此,但它也没有真正解决我的问题:
C++ -& CRTP . Type erasure vs polymorphism
请注意,我非常了解这两种技术。为此,我在下面提供了一个简单、独立的工作示例,如果觉得不必要,我很乐意将其删除。但是,该示例应阐明这两种技术对我的问题的意义。我对讨论命名法不感兴趣。另外,我知道编译时和运行时多态性之间的区别,尽管我认为这与问题无关。请注意,我对性能差异的兴趣不大,如果有的话。但是,如果有一个基于性能的引人注目的论点,我会很好奇阅读它。特别是,我想听听实际上只能使用这两种方法之一的具体示例(无代码)。
看看下面的例子,一个主要的区别是内存管理,对于多态性,它保留在用户端,而对于类型擦除,它被巧妙地隐藏起来,需要一些引用计数(或提升)。话虽如此,根据使用场景,多态示例的情况可能会通过使用带有向量 (?) 的智能指针来改善,尽管对于任意情况,这很可能会变得不切实际 (?)。另一个可能支持类型擦除的方面可能是通用接口的独立性,但为什么这会是一个优势(?)。
下面给出的代码已使用 MS VisualStudio 2008 进行了测试(编译和运行),只需将以下所有代码块放入单个源文件即可。它也应该在 Linux 上使用 gcc 编译,或者我希望/假设,因为我看不出为什么不 (?) :-) 为了清楚起见,我在这里拆分/划分了代码。
这些头文件应该足够了,对吧(?)。
#include <iostream>
#include <vector>
#include <string>
简单的引用计数来避免提升(或其他)依赖。该类仅在下面的类型擦除示例中使用。
class RefCount
{
RefCount( const RefCount& );
RefCount& operator= ( const RefCount& );
int m_refCount;
public:
RefCount() : m_refCount(1) {}
void Increment() { ++m_refCount; }
int Decrement() { return --m_refCount; }
};
这是简单的类型擦除示例/插图。它是从以下文章中复制和修改的。主要是我试图让它尽可能清晰和直接。 http://www.cplusplus.com/articles/oz18T05o/
class Object {
struct ObjectInterface {
virtual ~ObjectInterface() {}
virtual std::string GetSomeText() const = 0;
};
template< typename T > struct ObjectModel : ObjectInterface {
ObjectModel( const T& t ) : m_object( t ) {}
virtual ~ObjectModel() {}
virtual std::string GetSomeText() const { return m_object.GetSomeText(); }
T m_object;
};
void DecrementRefCount() {
if( mp_refCount->Decrement()==0 ) {
delete mp_refCount; delete mp_objectInterface;
mp_refCount = NULL; mp_objectInterface = NULL;
}
}
Object& operator= ( const Object& );
ObjectInterface *mp_objectInterface;
RefCount *mp_refCount;
public:
template< typename T > Object( const T& obj )
: mp_objectInterface( new ObjectModel<T>( obj ) ), mp_refCount( new RefCount ) {}
~Object() { DecrementRefCount(); }
std::string GetSomeText() const { return mp_objectInterface->GetSomeText(); }
Object( const Object &obj ) {
obj.mp_refCount->Increment(); mp_refCount = obj.mp_refCount;
mp_objectInterface = obj.mp_objectInterface;
}
};
struct MyObject1 { std::string GetSomeText() const { return "MyObject1"; } };
struct MyObject2 { std::string GetSomeText() const { return "MyObject2"; } };
void UseTypeErasure() {
typedef std::vector<Object> ObjVect;
typedef ObjVect::const_iterator ObjVectIter;
ObjVect objVect;
objVect.push_back( Object( MyObject1() ) );
objVect.push_back( Object( MyObject2() ) );
for( ObjVectIter iter = objVect.begin(); iter != objVect.end(); ++iter )
std::cout << iter->GetSomeText();
}
就我而言,这似乎使用多态实现了几乎相同的效果,或者可能不是(?)。
struct ObjectInterface {
virtual ~ObjectInterface() {}
virtual std::string GetSomeText() const = 0;
};
struct MyObject3 : public ObjectInterface {
std::string GetSomeText() const { return "MyObject3"; } };
struct MyObject4 : public ObjectInterface {
std::string GetSomeText() const { return "MyObject4"; } };
void UsePolymorphism() {
typedef std::vector<ObjectInterface*> ObjVect;
typedef ObjVect::const_iterator ObjVectIter;
ObjVect objVect;
objVect.push_back( new MyObject3 );
objVect.push_back( new MyObject4 );
for( ObjVectIter iter = objVect.begin(); iter != objVect.end(); ++iter )
std::cout << (*iter)->GetSomeText();
for( ObjVectIter iter = objVect.begin(); iter != objVect.end(); ++iter )
delete *iter;
}
最后是一起测试以上所有内容。
int main() {
UseTypeErasure();
UsePolymorphism();
return(0);
}
【问题讨论】:
-
您是否看过 Adobe 的
poly类或 Boost.TypeErasure(已接受但尚未发布)?除了 CRTP 之外,它们还实现了具有适当值语义的“基于概念”的多态性。 -
在我的书中,“多态性”和“类型擦除”都不是范式,它们只是语言工具箱中的工具。此外,我想不出一种方法来进行类型擦除不也使用多态性,所以我对你所追求的更加困惑。
-
J.N.,非常感谢,我会在适当的时候检查一下。原则上,我完全赞成重用,特别是在处理非商业或私有代码时,但是,对于商业代码,我总是在决定与减少外部依赖之间取得平衡,但这是另一个讨论...... :- ) 无论如何,我渴望了解更多...
-
如果你想要我的看法:只有当你需要一个相关的类型集合时才使用继承多态性只能在运行时决定,例如来自网络的消息包I/O,或 epoll 事件,或运行时选择的类注册表/工厂等。如果您绝对需要一个固定的单一类型来处理一组异构事物,请使用类型擦除,例如
std::shared_ptr<T>、@987654330 @ 或boost::any。在编译时执行其他所有操作。 -
Kerrek,正如我所写的,恕我直言,我对讨论命名法不感兴趣,称之为工具、方法、某种做事方式……我很清楚有些使用多态性在类型擦除示例中是“隐藏的”,我考虑将其放入我的问题中,但这不是重点。这两个例子展示了两种实现非常相似结果的方法(恕我直言)——一种优于另一种(?),它取决于使用场景(?),还是只是个人喜好和“语义糖”(? )。感谢您的第二条评论。