C++:创建共享对象而不是指向对象的共享指针

4

boost::shared_ptr真的让我很困扰。当然,我理解这种东西的实用性,但我希望我可以像使用A*一样使用shared_ptr<A>。考虑以下代码:

class A
{
public:
    A() {}
    A(int x) {mX = x;}
    virtual void setX(int x) {mX = x;}
    virtual int getX() const {return mX;}
private:
    int mX;
};


class HelpfulContainer
{
public:
    //Don't worry, I'll manager the memory from here.
    void eventHorizon(A*& a)
    {
        cout << "It's too late to save it now!" << endl;
        delete a;
        a = NULL;
    }
};


int main()
{
    HelpfulContainer helpfulContainer;

    A* a1 = new A(1);
    A* a2 = new A(*a1);
    cout << "*a1 = " << *a1 << endl;
    cout << "*a2 = " << *a2 << endl;
    a2->setX(2);
    cout << "*a1 = " << *a1 << endl;
    cout << "*a2 = " << *a2 << endl;
    cout << "Demonstrated here a2 is not connected to a1." << endl;

    //hey, I wonder what this event horizon function is.
    helpfulContainer.eventHorizon(a1);

    cout << "*a1 = " << *a1 << endl;//Bad things happen when running this line.
}

创建HelpfulContainer的人没有考虑到其他人想要保留对A对象的指针。我们不能给HelpfulClass boost::shared_ptr对象。但是我们可以使用pimlp习惯用法创建一个SharedA,它本身就是A:

class SharedA : public A
{
public:
    SharedA(A* a) : mImpl(a){}
    virtual void setX(int x) {mImpl->setX(x);}
    virtual int getX() const {return mImpl->getX();}
private:
    boost::shared_ptr<A> mImpl;
};

然后,主函数可能看起来像这样:

int main()
{
    HelpfulContainer helpfulContainer;

    A* sa1 = new SharedA(new A(1));
    A* sa2 = new SharedA(sa1);
    cout << "*sa1 = " << *sa1 << endl;
    cout << "*sa2 = " << *sa2 << endl;
    sa2->setX(2);
    cout << "*sa1 = " << *sa1 << endl;
    cout << "*sa2 = " << *sa2 << endl;
    cout << "this demonstrates that sa2 is a shared version of sa1" << endl;

    helpfulContainer.eventHorizon(sa1);
    sa2->setX(3);
    //cout << "*sa1 = " << *sa1 << endl;//Bad things would happen here
    cout << "*sa2 = " << *sa2 << endl; 
    //but this line indicates that the originally created A is still safe and intact.
    //only when we call sa2 goes out of scope will the A be deleted.
}

那么,我的问题是:上述模式是一个好的模式,还是我还没有考虑到什么。我的当前项目继承了一个类似上面的HelpfulContainer类,它正在删除我需要的指针,但我仍然需要 HelpfulContainer 中的数据结构。


更新:这个 问题 是一个后续问题。

如果 HelpfulContainer 想要接管指针,它应该使用正确的语义来完成。接口“eventHorizon”的命名不合适,因为它没有解释正在发生什么,并且它所采用的参数应该表明所有权正在被转移(可以使用 std::auto_ptr 或其新替代品 std::unique_ptr)。这两种情况都会表明 HelpfullContainer 对象正在接管对象,因此在调用之后,它将不再有效。所以你证明了一个事实:在 C++ 代码中,有些人不理解语言的语义,可能会编写糟糕的代码。 - Martin York
4个回答

6
shared_ptr 的主要作用是它(及其副本)拥有它所指向的对象。如果您想将一个 A 给一个管理其生命周期的容器,则根本不应该使用 shared_ptr,因为它不符合您的需求;HelpfulContainer 只知道如何成为动态创建对象的唯一所有者,因此您需要给它一个不被任何其他东西拥有的对象的指针。
我认为,一个对象关心自己的生命周期通常是设计不良(当然也有例外情况)。通常更有用的是,一个对象可以完成工作,而其他东西则管理其创建和销毁,并选择最简单的生命周期策略(例如局部/自动变量)。
如果您绝对必须在两个不协作的事物之间共享所有权(例如 shared_ptrHelpfulContainer),那么您将不得不使用某种代理技术。
但在这种情况下,看起来 HelpfulContainer 对于您的情况并不是很有帮助。

1
+1:只需更改那个该死的容器(并仔细检查使用它的代码),例如添加一个接受shared_ptr的重载可能会很有用。 - Matthieu M.

1

我不确定这对你有什么帮助。

如果 helpfulContainer.eventHorizon() 总是删除其参数,那为什么不传递一个 (原始的) A 类的新副本呢:

  helpfulContainer.eventHorizon(new A(sa1));

或者,如果helpfulContainer.eventHorizon()仅在某些情况下删除其参数,则调用方式为

  helpfulContainer.eventHorizon(new SharedA(sa1)); 

在那些选择不删除的情况下,将会泄漏出SharedA和原始A(sa1) 两者


如果 eventHorizon 没有删除指针而是将它存储在某个数据结构中,那么会发生什么?如果所有相关的 shared_ptr 都已经被删除,那么数据结构中就会隐藏一个无效指针。 - JnBrymn
如果你知道eventHorizon将会接管指针并最终删除它,而你需要保留所有权,则应该传递一个新的副本,就像我展示的第一种形式一样(helpfulContainer.eventHorizon(new A(sa1));)。 - Dan Breslau
@John Berryman:也许我误解了你的评论。对于你的第一句话:如果eventHorizon持有指针,并在其方便时删除它,而你仍然需要访问同一个对象,则你的建议可能有所帮助。但是,您需要非常彻底地重构代码,以便除了通过SharedA对象之外,永远不会使用指向原始A对象的指针。任何尝试混合两者都将导致大量哀嚎和牙 gnashing。我认为您还需要一个专门的复制构造函数SharedA(const SharedA&),该函数将复制mImpl - Dan Breslau
@John Berryman:对于你的第二句话,我不理解你在这里的意思。如果确实所有 shared_ptrs 都被删除(容器内部和外部都是)- 我假设你所说的是 "destroyed" 而不是 "deleted",因为 boost::shared_ptr 是一个对象 - 那么你就没有任何无效指针了。除非你将原始指针和 shared 指针混用于同一对象...但你永远不想那样做。 - Dan Breslau

0

所以你正在创建一个可以删除的替身(SharedA)。尽管这有点尴尬,但我想这是必要的,以便与您的遗留API一起使用。为了稍微改进这个问题:允许从shared_ptr构建SharedA,但不能反过来 - 然后只在绝对必要时使用SharedP:

int main()
{
  HelpfulContainer helpfulContainer;

  boost::shared_ptr<A> sa1(new A(1));

  // deletes its parameter, but that's okay
  helpfulContainer.eventHorizon(new SharedA(sa1)); 
}

0

将底层指针类型的隐式转换与shared_ptr的预期使用不一致,因为您可以非常容易地将shared_ptr传递给函数等而不会意识到它。

对我来说,HelpfulContainer听起来一点也不有用,应该修复或放弃。

如果不可能的话,最好的方法可能就是复制要传递的A并将副本传递给容器。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接