4

我使用普通指针编写了一个项目,现在我厌倦了手动内存管理。

在重构过程中可以预见到哪些问题?

到现在为止,我已经花了一个小时来替换X*shared_ptr<X>想要自动管理内存的类型。然后我dynamic_cast改为dynamic_pointer_cast. 我仍然看到更多错误(与NULL传递this给函数相比)。

我知道这个问题有点模糊和主观,但我认为我可以从已经这样做的人的经验中受益。

有没有一些陷阱?

4

5 回答 5

4

尽管在任何地方都可以轻松使用boost::shared_pointer,但您应该根据所有权语义使用正确的智能指针。

在大多数情况下,您将希望std::unique_ptr默认使用,除非所有权在多个对象实例之间共享。

如果您遇到周期性所有权问题,您可以使用boost::weak_ptr.

还要记住,在传递 shared_ptr 时,出于性能原因(避免原子增量),您应该始终通过 const 引用传递它们,除非您真的想将所有权授予不同的实体。

于 2012-05-08T22:00:15.437 回答
3

有没有一些陷阱?

是的,根据墨菲定律,如果你盲目地用 shared_ptr 替换每个指针,结果会证明这不是你想要的,你将在接下来的 6 个月里寻找你引入的错误。

在重构过程中可以预见到哪些问题?

内存管理效率低下,未使用的资源存储时间过长,内存泄漏(循环引用),无效引用计数(相同的指针分配给多个不同的 shared_pointers)。

不要盲目地用 shared_ptr 替换所有内容。仔细研究程序结构并确保 shread_ptr 是必需的,它代表了你想要的。

此外,请确保您使用支持简单分支(git 或 mercurial)的版本控制,因此当您破坏某些内容时,您可以恢复到以前的状态或运行类似于“git bisect”的内容来定位问题。

显然你需要用 shared_ptr 替换 X*

错误的。这取决于上下文。如果您有一个指向某个数组中间的指针(例如,像素数据操作),那么您将无法用 shared_ptr 替换它(并且您不需要)。只有在需要确保对象的自动释放时才需要使用 shared_ptr。对象的自动释放并不总是您想要的。

于 2012-05-08T23:32:59.000 回答
2

如果你想坚持使用 boost,你应该考虑是需要 boost::shared_ptr 还是 boost::scoped_ptr。shared_ptr 是类之间共享的资源,而 scoped_ptr 听起来更像您可能想要的(至少在某些地方)。scoped_ptr 超出范围时会自动删除内存。

将 shared_ptr 传递给函数时要小心。shared_ptr 的一般规则是按值传递,以便创建副本。如果你通过引用传递它,那么指针的引用计数将不会增加。在这种情况下,您最终可能会删除一段您希望保持活动状态的内存。

但是,在某些情况下,您可能希望通过引用传递 shared_ptr。也就是说,如果您希望在不同的函数中分配内存。在这种情况下,只需确保调用者在它调用的函数的生命周期内仍然持有指针。

void allocPtr( boost::shared_ptr< int >& ptrByRef )
{
    ptrByRef.reset( new int );
    *ptrByRef = 3;
}

int main()
{
    boost::shared_ptr< int >& myPointer;
    // I want a function to alloc the memory for this pointer.

    allocPtr( myPointer ); // I must be careful that I still hold the pointer
                           // when the function terminates

    std::cout << *ptrByRef << std::endl;
}
于 2012-05-09T04:27:28.690 回答
2

我列出了所涉及的步骤/问题。他们为我工作,但我不能保证他们是 100% 正确的

0) 检查是否有循环共享指针。如果是这样,这会导致内存泄漏吗?我的情况,幸运的是,循环不需要被打破,因为如果我有一个循环,循环中的对象是有用的,不应该被破坏。使用弱指针打破循环

1)您需要将“大多数”替换X*shared_ptr<X>. shared_ptr (仅?)在每次动态分配 X 后立即创建。在所有其他时间,它是复制构造的,或者是用空指针构造的(以发出 NULL 信号)。为了安全起见(但效率有点低),请仅通过引用传递这些 shared_ptrs 。无论如何,您可能从未通过引用传递您的指针以开始 => 不需要额外的更改

2)您可能dynamic_cast<X*>(y)在某些地方使用过。将其替换为 dynamic_pointer_cast<X>(y)

3)无论你通过哪里NULL(例如,表示计算失败),传递一个空的共享指针。

4) 删除相关类型的所有删除语句

5)让你的基类 B 继承自enable_shared_from_this<B>. 然后无论你经过this,通过,shared_from_this()。如果函数需要派生类型,您可能必须进行静态转换。请记住,当您调用时shared_from_this(),一些shared_ptr必须已经拥有this。特别是,不要调用shared_from_this()类的构造函数

我确信可以半自动化这个过程以获得语义上等效但不一定非常有效的代码。程序员可能只需要推理循环引用(如果有的话)。

我在许多这些步骤中使用了很多正则表达式。大约花了3-4个小时。到目前为止,代码已编译并正确执行。

于 2012-05-09T23:24:22.370 回答
0

有一个工具可以尝试自动转换为智能指针。我从来没有尝试过。这是以下论文摘要的引述: http ://www.cs.rutgers.edu/~santosh.nagarakatte/papers/ironclad-oopsla2013.pdf

为了强制执行难以静态检查的安全属性,Ironclad C++ 通过模板化的“智能指针”类应用动态检查。使用半自动重构工具,我们已将近 50K 行代码移植到 Ironclad C++

于 2014-10-06T03:51:41.903 回答