我最近使用了很多不同的boost智能指针,以及普通的指针。我已经注意到,随着您的开发,您往往会意识到,您必须切换指针类型和内存管理机制,因为您忽略了一些循环依赖或一些小烦人的事情。当这种情况发生,并且您更改指针类型时,您必须改变大量的方法签名才能接受新的指针类型,或者在每个调用站点之间必须在指针类型之间进行转换。如果您有相同的函数,但希望它采用多个指针类型,也会出现问题。
我想知道是否已经存在一种通用的处理方法,即对传递给它的指针类型不可知的写方法?
显然,我可以找到一些方法来做到这一点,其中之一就是为每个指针类型编写重载的方法,但这很快就变成了麻烦。另一种方法是使用带有某种类型推断的模板样式解决方案,但这将导致编译代码中的一些显着膨胀,并且可能会引发奇怪的无法解决的模板错误。
我的想法是编写一个新的类any_ptr<T>,其中包含来自所有主要指针类型的转换构造函数,比如T*、shared_ptr<T>、auto_ptr<T>、scoped_ptr<T>甚至weak_ptr<T>,然后让它公开*和->操作符。通过这种方式,它可以用于不返回函数外部的指针的任何函数,并且可以与任何公共指针类型的组合一起调用。
我的问题是,这是否真的是一件愚蠢的事情?我看到它可能被滥用,但假设它从未用于返回any_ptr的函数,那么有什么大问题吗?请你的想法。
编辑1
在阅读了你的回答后,我想做一些评论太长的笔记。
首先是关于使用原始指针或引用(@shoosh)的。我同意您可以让函数使用原始指针,但是假设我使用的是shared_ptr,这意味着我必须在每个调用站点上使用ptr.get(),现在假设我实现了循环引用,我必须更改指向weak_ptr的指针,然后我必须去将所有这些调用站点更改为x.lock().get()。现在我同意这不是一场灾难,但这是令人恼火的,我觉得有一个优雅的解决办法。对于传递as T& references和going *x,也可以这样说,类似的呼叫站点更改将不得不进行。
我在这里要做的是让代码读起来更优雅,甚至通过指针类型的大变化也更容易重构。
第二次关于smart_ptr语义:我同意不同的智能指针是出于不同的原因使用的,并且在复制和存储方面必须考虑某些问题(这就是为什么boost::shared_ptr<T>不能自动转换为T*的原因)。
但是,我设想any_ptr (回想起来可能是个不好的名称)只在指针不会被存储的情况下使用(除了堆栈上的临时变量之外)。它应该从各种智能指针类型隐式构造,重载*和->运算符,并转换为T* (通过自定义转换函数T*())。这样,any_ptr的语义与T*完全相同。因此,它应该只用于使用原始ptr是安全的地方(@ Alexandre_C在评论中是这么说的)。这也意味着,@Matthieu_M所说的“重型机器”将不复存在。
第三,关于模板,。虽然模板对某些事情很好,但出于上述原因,我对它们持谨慎态度。
最终:因此,基本上我要做的是,对于函数,通常使用原始ptr的(T*)作为参数,我想要创建一个系统,在这个系统中,这些参数可以自动接受任何不同的smart_ptr类型,而无需在调用站点进行转换。我之所以想这样做,是因为我认为它将通过消除转换cruft来提高代码的可读性(因此也会稍微短一些,尽管不会太多),而且它将减少重构和尝试不同智能指针机制的麻烦。
也许我应该叫它unmanaged_ptr而不是any_ptr。这将更准确地描述语义。我为这个下流的名字道歉。
编辑2
好的,这是我想上的课。我叫它dumb_ptr。
template<typename T>
class dumb_ptr {
public:
dumb_ptr(const dumb_ptr<T> & dm_ptr) : raw_ptr(dm_ptr.raw_ptr) { }
dumb_ptr(T* raw_ptr) : raw_ptr(raw_ptr) { }
dumb_ptr(const boost::shared_ptr<T> & sh_ptr) : raw_ptr(sh_ptr.get()) { }
dumb_ptr(const boost::weak_ptr<T> & wk_ptr) : raw_ptr(wk_ptr.lock().get()) { }
dumb_ptr(const boost::scoped_ptr<T> & sc_ptr) : raw_ptr(sc_ptr.get()) { }
dumb_ptr(const std::auto_ptr<T> & au_ptr) : raw_ptr(au_ptr.get()) { }
T& operator*() { return *raw_ptr; }
T * operator->() { return raw_ptr; }
operator T*() { return raw_ptr; }
private:
dumb_ptr() { }
dumb_ptr<T> operator=(const dumb_ptr<T> & x) { }
T* raw_ptr;
};它可以从普通的智能指针自动转换,并且可以被视为原始的T*指针,而且可以自动转换为T*。默认构造函数和赋值运算符(=)已被隐藏起来,以阻止人们将其用于函数参数以外的任何东西。当用作函数参数时,可以执行以下操作。
void some_fn(dumb_ptr<A> ptr) {
B = ptr->b;
A a = *ptr;
A* raw = ptr;
ptr==raw;
ptr+1;
}这几乎就是你想用指针做的所有事情。它的语义与原始指针T*完全相同。但是现在它可以与任何智能指针一起使用作为参数,而不必在每个调用站点重复转换代码(.get、.lock)。另外,如果您更改了您的智能指针,您就不必到处修改每个呼叫站点。
现在我认为这是相当有用的,我看不出有什么问题吗?
发布于 2011-04-05 23:56:14
回答我自己的问题,这样它就不会出现在未回答的列表中。基本上,其他任何一个答案都没有发现我的想法中有一个严重的缺陷。@Alexandre C.的道具来理解我想做的事情。
发布于 2011-03-16 12:55:54
有了这样一个类any_ptr,除了*和->之外,您几乎什么也做不了。不得转让、复制、复制或销毁。如果这就是您所需要的,那么只需编写函数,以原始指针T*作为参数,然后在自动指针上调用它,使用.get()或诸如此类的东西。
发布于 2011-03-16 13:10:06
您可以使用不同的智能指针,因为它们提供了不同的语义和权衡。
any_ptr的语义是什么?如果它来自unique_ptr / scoped_ptr,则不应该复制它。如果它来自shared_ptr或weak_ptr,它将需要他们的重型机器作为参考计数。
你会有一个类型与所有的缺点(沉重,不可复制),以获得很少的好处..。
这里有一个接口问题。除非方法管理对象的生存期(在这种情况下,需要精确的指针类型),否则它不需要知道如何管理此生存期。
这意味着仅作用于对象的方法应该接受指针或引用(取决于它是否为null),并相应地调用:
void foo(T* ptr);,称为shared_ptr<T> p; foo(p.get());void foo(T& ptr);,称为foo(*p);注意:第二个接口与指针无关。
这是简单的封装:一个方法只需要知道它需要操作的最小值。如果它不管理生存期,则向方法公开如何管理此生存期.引起你亲眼目睹的涟漪。
https://stackoverflow.com/questions/5325471
复制相似问题