【问题标题】:C++: pass shared ptr by reference without breaking the sharing?C ++:通过引用传递共享ptr而不破坏共享?
【发布时间】:2020-06-19 14:45:03
【问题描述】:

假设我使用智能指针?好的,太好了:

shared_ptr<whatever>(new whatever());

立即,我想做以下事情,而不是纠结于 C++ 的单返回项目政策和其中的语义混乱:

void do_something(whatever *& A, whatever *& B){
  auto Aptr = shared_ptr<whatever>(new whatever());
  A = Aptr.get();

  auto Bptr = shared_ptr<whatever>(new whatever());
  B = Bptr.get();
}

但是,shared_ptr 机制会删除对象,因为它们是通过引用从函数中传递出来的,因此,当我调用代码中的指针时,

whatever *A,*B;
do_something(A,B);

通过引用获取设置到合适的地址,数据已经被抹杀了。


所有这些都是为了避免在返回类型中键入和文档的混乱,因为在列表中累积参数是很自然的......但是返回托盘中的每个附加项目都会在文档中拆分上下文提示。

访问通过引用初始化的值完全是一种理想的机制。


另外,我想补充一点,只要我在调用范围内拥有带有共享指针的指针,我就已经让这个机制起作用了。

但是,这是对同一问题的递归重复:我想自动管理函数内部的指针并通过引用外部变量来返回它们,这样就有了普通 C 指针的外观和感觉,但没有mallocs 或在外部范围释放——我只是声明指针并通过引用传递它们。

相反,我为每个指针设置了 2 行:声明、构造它们内部的函数,以及在函数调用后拥有它们的共享 ptr。只是另一个版本的无 malloc 机制。

理想情况下,有一种方法可以删除我使用共享指针拥有 ptr 的第二行,或者(如果不可能的话),一种在声明共享指针后在函数内部初始化共享指针的方法外。请注意,这里的目标是:

void func(double * a, int * b, T * c);

double *a; 
int *b; 
T *c;
func(a,b,c); 
// no headaches whatsoever at this scope.

// versus

double *a; 
int *b; 
T *c;
func(a,b,c);
shared_ptr<double> asp(a);
shared_ptr<int> bsp(b);
shared_ptr<T> csp(c,[](T*_){/*special T deletion process*/;};

// versus

double *a; 
int *b; 
T *c;
func(a,b,c);

/* solve the universe */

free(a);
free(b);
delete(T);

【问题讨论】:

  • 使用多返回值而不是输出参数。
  • 为什么不使用返回值,比较isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-out。您可以返回 structpair,甚至是 pairshared_ptr。顺便说一句,为什么shared_ptrunique_ptr 还不够吗?
  • 我不太明白“普通 C 指针的外观和感觉,但在外部范围内没有 malloc 或释放”的目的 - 这对 C 和 C++ 可能同样令人困惑/可疑程序员。
  • @Hulk 整个 C++ 世界同样令人困惑和可疑。您的观点被视为根本不使用 C/C++ 的论据
  • @Hulk 这里的重点是控制语言语义。当然,智能指针减少了内存错误的可能性,但增加了一层包装语义和一堆代码重复。我们的想法是要有一个标准,并遵守它。

标签: c++ c pointers


【解决方案1】:

也许你的问题可以通过一个或两个构造函数的结构来解决:

struct NoNeadachesWhatsoever {
    shared_ptr<double> asp;
    shared_ptr<int> bsp;
    shared_ptr<T> csp;

    NoNeadachesWhatsoever() {
        // initialize members.
    }
};

// ...

NoNeadachesWhatsoever no_headaches_whatsoever;

【讨论】:

  • 这是我已经在的地方。这正是我所拥有的。所以堆栈如下:一个较低级别的 C 库,一个必须使用原始指针并且确实可以使用原始指针的上层 C++ 扩展,以及一个由类组成的 C++ 层。但是,我想处理 C 库接口上的所有内存问题。但是,根据您的回复,看来我必须将共享指针保存在任何顶层的范围内,并且只能通过类中的私有/公共机制来呈现指针的原始语义,以将共享指针保持在范围内...可以安全地承诺吗?
  • @Christopher 您可以从智能指针中提取原始指针并将其传递给 C API。
  • @Christopher 智能指针管理对象的生命周期,因此当您仍然需要智能指针管理的对象时,您需要保持智能指针处于活动状态。这是智能指针为您管理生命周期的唯一目的,因此您无需手动清理。一些有用的阅读theboostcpplibraries.com/boost.smartpointers
  • 正确,完全正确。问题相当于:如何通过返回或引用共享指向祖先作用域的指针的所有权,以便智能指针语义可以隐藏。否则,我要添加一个东西,使一项任务更容易,而另一项任务难度大致相等。我相信 QT 智能指针实现可以将原始指针作为原始指针共享到外部范围,并且出于用户的目的而消失……但这已连接到 QT 对象系统中。问题是:标准可以做到这一点吗?
  • 您可以对嵌入引用计数器的对象执行 smart->raw->smart,请参阅boost::intrusive_ptr。但是,这可能会导致不正确地维护引用计数器,并可能导致泄漏或早期破坏,这违背了使用智能指针的初衷。
【解决方案2】:

只要在任何地方统一使用共享指针:

void do_something(std::shared_ptr<whatever> &A, std::shared_ptr<whatever> &B) {
           :

【讨论】:

  • 打字很多
  • 如果打字太麻烦,请定义一个较短的别名:template&lt;class T&gt; using P&lt;T&gt; = std::shared_ptr&lt;T&gt;;
【解决方案3】:

我有一个不好的答案,但它会做你想要的。

void do_something( whatever *& A, whatever *& B ) {
    static std::shared_ptr<whatever> ptr_a;
    static std::shared_ptr<whatever> ptr_b;
    ptr_a.reset( new whatever );
    ptr_b.reset( new whatever );
    A = ptr_a.get();
    B = ptr_b.get();
}

它会在你下次调用函数或程序时释放你的内存 结束,所以取决于你需要多少内存......

我想我会写它,即使它是一个糟糕的答案。

另一个不错的答案是有这样的课程:

class Shallocator
{
public:
    Shallocator( whatever *& A, whatever *& B ) {
        ptr_a.reset( new whatever );
        ptr_b.reset( new whatever );
        A = ptr_a.get();
        B = ptr_b.get();
    }
private:
    std::shared_ptr<whatever> ptr_a;
    std::shared_ptr<whatever> ptr_b;
};

然后在你去的函数中:

whatever * A, * B;
Shallocator { A, B };

// no headaches

Shallocator 的生命周期结束时,内存将被释放。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-11
    • 1970-01-01
    • 2023-01-08
    相关资源
    最近更新 更多