使用通用方法有什么好处和缺点(在编译时,运行时,性能和内存中)?
答案 0 :(得分:8)
好的,Java 泛型和C ++ 模板是如此不同,我不确定是否可以在一个问题中回答它们。
这些几乎都是语法糖。它们是通过一项名为type erasure的有争议的决定来实施的。他们真正做的就是阻止你不得不投入很多东西,这使得它们更安全。性能与制作专用类相同,除非您使用的是原始数据类型(int,float,double,char,bool,short)。在这些情况下,值类型必须装箱到相应的引用类型(Integer,Float,Double,Char,Bool,Short),这有一些开销。内存使用情况相同,因为JRE只是在后台执行转换(基本上是免费的)。
Java也有一些不错的类型covariance and contravariance,这使得事情看起来比不使用它们更清晰。
这些实际上根据输入类型生成不同的类。 std::vector<int>
是一个与std::vector<float>
完全不同的类。不支持协方差或逆变,但支持将非类型传递给模板,部分模板专业化。他们基本上允许你做任何你想做的事。
但是,由于C ++模板为其模板参数的每个变体创建不同的类,因此编译的可执行文件的大小更大。除此之外,编译时间也会大大增加,因为每个编译单元都必须包含所有模板代码,并且必须生成更多代码。但是,实际的运行时内存占用量通常小于替代值(释放额外的void *)并且性能更好,因为编译器可以使用已知类型执行更积极的优化。
编辑(感谢DavidRodríguez):虽然通用Java类编译它的全部内容,但在使用C ++模板时,您只能编译您使用的内容。因此,如果您创建std::vector<int>
并仅使用push_back
和size
,则只会将这些函数编译到目标文件中。这减轻了可执行问题的大小。
如果您对它们之间的差异感到好奇,请查看C#,Java和C ++中的this comparison泛型。
答案 1 :(得分:2)
在Java中(不确定C ++)泛型是一个编译时功能。他们避免使用可能不安全的演员阵容。例如,集合所拥有的类型明确地可供编译器使用,以便它知道可以在其中放置哪种对象/基元。这消除了开发人员在某个时间对集合中可能存在的内容所做出的不安全假设。它还有助于提高代码的可读性。在Java中,我不相信有性能或内存增益。
答案 2 :(得分:2)
让我们忘记运行时的好处,因为这将是一个不成熟的优化。
在编译时,泛型方法可以显着提高可读性,作为奖励,你会发现很多错误(在编译时而不是在运行时)。当然,所有这一切的先决条件是你必须尽可能正确地定义通用,而不是太松散而不是太紧。
答案 3 :(得分:1)
在编码期间,好处是您不必将对象转换为特定类型,因此有一定的编译时间安全性。在运行时没有区别(在Java中)。
答案 4 :(得分:0)
两种语言的主要好处是类型安全:例如,编译器将保证您的List<Foo> list
仅包含Foo
个对象。如果没有泛型(例如在早期版本的Java中),您的列表将接受任何继承Object
的类。
性能方面的Java与C ++不同。在C ++中,泛型类在编译过程中是专用的,因此根本没有开销。
另一方面,在Java中,泛型是在现有JVM规范之上实现的,因此编译器会生成实际使用类型转换的Java字节代码,这些代码不是免费的。另一方面,泛型的替代方法是将所有内容都视为Object
,这也需要类型转换,因此Java中这两种备选方案之间并没有真正的性能差异。
最后,有些相关的,当在Java中引入泛型时,它们还添加了自动装箱,允许您使用带有泛型的原始类型。自动装箱意味着基本类型在其等效类中自动加框,即当int
对象在通用上下文中使用时,Integer
被加框。这再次增加了运行时开销,因为必须首先创建Integer
的新实例,然后再收集垃圾。
答案 5 :(得分:0)
我编写了一些通用字段和记录类。他们不使用模板。一个很酷的属性是他们使用访客设计模式的read
和write
方法。
一个很好的属性是您可以在不知道细节的情况下处理记录。 (BWT,记录由一个或多个字段组成)。因此,通过将 Reader (读取字段的Visitor类)传递给每个字段并让字段使用给定的读取器来填充其成员来读取记录。与写作相似。
如果我需要从XML文件或数据库中读取数据,我只需创建一个专门从XML或数据库中读取的 Reader 。这不需要更改Record或Field类。很好,快捷,方便。
一个缺点是我无法在调试器中轻松查看记录。我必须编写代码来打印记录,或者在遇到特定记录时使用if
语句来捕获。
我很惊讶在不知道对象的细节和坚持接口的情况下可以执行多少工作。
答案 6 :(得分:0)
与大多数Java开发人员的信念相反,您可以避免类型擦除,您可以使用Generics来实现类型化。
这确实是一个技巧,但如果你真的需要更接近C ++模板的话,就可以做到。
http://www.jquantlib.org/index.php/Using_TypeTokens_to_retrieve_generic_parameters
我希望它有所帮助。
答案 7 :(得分:-1)
你也提出了一些弊端,这是一个。
C ++中的通用编程可以产生一些漂亮的“太空时代”代码,这些代码可能非常冗长且难以阅读。由人类领悟。也就是说,人类不是设计它的人。因此,维护和使用可能具有挑战性。难以维护或使用的代码对其有很大的打击。我发现这一点特别正确的一个地方是使用policy classes。
这是一个例子。前段时间,我写了一个基于策略的资源管理器。有点像智能指针,但通用到足以用于任何类型的资源,而不仅仅是内存。像互斥体,GDI(Windows)资源等等。写这个的动机是双重的。一,我只是想写它:)但是两个,我想创建一个代码库,通常可以用来管理各种资源。为了使它普遍有用,人们不得不想要使用它。
所以让我问你,你想用这个吗?
/*** COPY POLICIES ***/
class SimpleCopyPolicy
{
public:
template<class Resource> Resource copy(const Resource& rhs) const { Resource ret = rhs; return ret; }
protected:
~SimpleCopyPolicy(){};
};
class DuplicateHandleCopyPolicy
{
public:
HANDLE sourceProcess, targetProcess;
DWORD access, options;
BOOL inherit;
DuplicateHandleCopyPolicy(HANDLE sourceProcess_=GetCurrentProcess(), HANDLE targetProcess_=GetCurrentProcess(), DWORD access_=0, BOOL inherit_=FALSE,DWORD options_=DUPLICATE_SAME_ACCESS)
: sourceProcess(sourceProcess_), targetProcess(targetProcess_), access(access_), inherit(inherit_), options(options_) {}
template<class Resource> Resource copy(const Resource & rhs) const
{
Resource ret;
# if defined(VERBOSE_STLEXT_DEBUG) & defined(MHDAPI)
if( !verify( DuplicateHandle(sourceProcess, rhs, targetProcess, &ret, access, inherit, options) ))
{
DWORD err = GetLastError();
mhd::WarningMessage("DuplicateHandleCopyPolicy::copy()", "Error %d Copying Handle %X : '%s'",
err, rhs, stdextras::strprintwinerr(err).c_str() );
}
else
mhd::OutputMessage("Duplicated %X to %X", rhs, ret);
# else
DuplicateHandle(sourceProcess, rhs, targetProcess, &ret, access, inherit, options);
# endif
return ret;
}
protected:
~DuplicateHandleCopyPolicy(){};
};
/*** RELEASE POLICIES ***/
class KernelReleasePolicy
{
public:
template<class Handle> bool release(Handle& h)
{
# if defined(VERBOSE_STLEXT_DEBUG) & defined(MHDAPI)
OutputMessage("Closing %X", h);
# endif
return 0 != CloseHandle(h);
}
};
class CritsecReleasePolicy
{
public:
template<class Handle> bool release(Handle& h)
{
DeleteCriticalSection(&h);
return true;
}
protected:
~CritsecReleasePolicy() {};
};
class GDIReleasePolicy
{
public:
template<class Handle> bool release(Handle h) { return 0 != DeleteObject(h); }
protected:
~GDIReleasePolicy(){};
};
class LibraryReleasePolicy
{
public:
template<class Handle> bool release(Handle h) { return 0 != FreeLibrary(h); }
protected:
~LibraryReleasePolicy(){};
};
# ifdef WINSOCK_VERSION
class SocketReleasePolicy
{
public:
template<class Handle> bool release(Handle h) { return 0 != closesocket(h); }
protected:
~SocketReleasePolicy(){};
};
# endif
class DestroyWindowPolicy
{
public:
template<class Handle> bool release(Handle h) { return 0 != DestroyWindow(h); }
protected:
~DestroyWindowPolicy() {};
};
/*** LOCKING POLICIES ***/
class WFSOPolicy // Wait For Single Object
{
public:
WFSOPolicy(DWORD timeout_=INFINITE) : timeout(timeout_) {};
template<class Handle> bool wait(Handle& h) const
{
# if defined(VERBOSE_STLEXT_DEBUG) & defined(MHDAPI)
DWORD ret = ::WaitForSingleObject(h,timeout);
if( !verify( WAIT_OBJECT_0 == ret ))
{
DWORD err = GetLastError();
# ifdef UNICODE
mhd::WarningMessage("WFSOPolicy", "Error %d Waiting for object %X [Timeout %s] : '%S'",
err, h, INFINITE==timeout?"INFINITE":std::formatstr("%d ms", timeout).c_str(),
stdextras::strprintwinerr(err).c_str() );
# else
mhd::WarningMessage("WFSOPolicy", "Error %d Waiting for object %X [Timeout %s] : '%s'",
err, h, INFINITE==timeout?"INFINITE":std::formatstr("%d ms", timeout).c_str(),
stdextras::strprintwinerr(err).c_str() );
# endif
return false;
}
return true;
# else
return WAIT_OBJECT_0 == ::WaitForSingleObject(h,timeout);
# endif
}
DWORD timeout;
};
/*** LOCK/UNLOCK POLICIES ***/
class CritsecLockPolicy // CRITICAL_SECTION lock/unlock policies
{
public:
template<class Handle> bool lock(Handle& h)
{
EnterCriticalSection(const_cast<CRITICAL_SECTION*>(&h));
return true;
}
template<class Handle> bool unlock(Handle& h)
{
LeaveCriticalSection(&h);
return true;
}
};
template<DWORD waitTimeout = INFINITE>
class MutexLockPolicy : public WFSOPolicy
{
public:
MutexLockPolicy() : WFSOPolicy(waitTimeout) {};
template<class Handle> bool lock(Handle& h) const
{
return wait(h);
}
template<class Handle> bool unlock(Handle& h) const
{
return 0 != ReleaseMutex(h);
}
};
class PlaceboLockPolicy // this lock policy doesnt actually do anything! useful for debugging & experimentation
{
public:
PlaceboLockPolicy() {};
template<class Handle> bool lock(Handle&) const
{
return true;
}
template<class Handle> bool unlock(Handle&) const
{
return true;
}
};
template<class Resource, typename ReleasePolicy, typename CopyPolicy = SimpleCopyPolicy>
class simple_auto_resource : public ReleasePolicy, public CopyPolicy
{
public:
typedef simple_auto_resource<Resource,ReleasePolicy,CopyPolicy> base_type;
simple_auto_resource() : res(0) {}
simple_auto_resource(const Resource & r) : res(copy(r)) {}
~simple_auto_resource() { if(res) release(res); }
void clear() { if(res) release(res); res = 0; }
Resource& get() { return res; }
const Resource& get() const { return res; }
Resource detach() { Resource ret = res; res = 0; return ret; }
operator const Resource&() const { return get(); }
operator Resource&() { return get(); }
base_type& operator=(const Resource& rhs) { clear(); res = copy(rhs); return * this; }
template<class Comp> bool operator==(const Comp& rhs) const { return res == (Resource)rhs; }
template<class Comp> bool operator!=(const Comp& rhs) const { return res != (Resource)rhs; }
template<class Comp> bool operator<(const Comp& rhs) const { return res < (Resource)rhs; }
private:
Resource res;
};
typedef simple_auto_resource<HBRUSH,GDIReleasePolicy> auto_brush;
typedef simple_auto_resource<HINSTANCE, LibraryReleasePolicy> auto_lib;
typedef simple_auto_resource<CRITICAL_SECTION, CritsecReleasePolicy> auto_critsec;
typedef simple_auto_resource<HWND,DestroyWindowPolicy> auto_destroy_hwnd;
typedef simple_auto_resource<HANDLE,KernelReleasePolicy,DuplicateHandleCopyPolicy> auto_kernelobj;
# ifdef WINSOCK_VERSION
typedef simple_auto_resource<SOCKET,SocketReleasePolicy> auto_socket;
# endif
typedef auto_kernelobj auto_mutex;
typedef auto_kernelobj auto_event;
typedef auto_kernelobj auto_filehandle;
typedef simple_auto_resource<HANDLE,KernelReleasePolicy> auto_localkernelobj;
typedef simple_auto_resource<HANDLE,KernelReleasePolicy> auto_localmutex;
typedef simple_auto_resource<HANDLE,KernelReleasePolicy> auto_localevent;
typedef simple_auto_resource<HANDLE,KernelReleasePolicy> auto_thread;
typedef simple_auto_resource<HMODULE,KernelReleasePolicy> auto_hmodule;
如果你的答案是“不,太复杂”,那正是我的观点。