【问题标题】:C++: Creating a shared object rather than a shared pointer to an objectC++:创建共享对象而不是指向对象的共享指针
【发布时间】:2010-12-10 16:08:45
【问题描述】:

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

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 中存在的数据结构。


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

【问题讨论】:

  • 如果 HelpfulContainer 想要获得指针的所有权,它应该使用正确的语义来这样做。接口eventHorizon 的名字很糟糕,因为它没有解释正在发生的事情,并且它所采用的参数应该表明所有权正在转移(比如 std::auto_ptr 或其新的替代品 std::unique_ptr)。这两个都表明 HelpfullContainer 对象正在获取该对象的所有权,因此在调用后它将不再有效。所以你证明了一点,在 C++ 代码中,不了解语言语义的人可能会写得很糟糕。

标签: c++ shared-ptr smart-pointers pimpl-idiom


【解决方案1】:

shared_ptr 的全部意义在于它(及其副本)拥有它所指向的对象。如果您想为管理其生命周期的容器提供A,那么您根本不应该使用shared_ptr,因为它不能满足您的需求; HelpfulContainer 只知道如何成为动态创建的对象的唯一所有者,因此您需要给它一个指向不属于其他任何对象的对象的指针。

我认为通常一个对象关心它自己的生命周期是很糟糕的设计(也有例外)。如果一个对象可以完成一项工作,而其他东西管理它的创建和销毁,选择最简单的生命周期策略(例如本地/自动变量),通常会更有用。

如果您绝对必须在两个不合作的事物之间共享所有权(例如 shared_ptrHelpfulContainer),那么您将不得不使用某种代理技术。

不过,在这种情况下,HelpfulContainer 似乎对您的情况没有多大帮助。

【讨论】:

  • +1:只需更改该死的容器(并仔细检查使用它的代码),例如添加一个采用shared_ptr 的重载可能很有用。
【解决方案2】:

我不确定这对你有什么作用。

如果helpfulContainer.eventHorizon() always 删除了它的参数,那为什么不只传递一个(原始)A 类的新副本:

  helpfulContainer.eventHorizon(new A(sa1));

或者,如果helpfulContainer.eventHorizon() 只是有时删除它的参数,然后调用

  helpfulContainer.eventHorizon(new SharedA(sa1)); 

在选择不删除的情况下会泄露 SharedA 和原始 A (sa1)。

【讨论】:

  • eventHorizo​​n 不会删除指针,而是将其存储在某个数据结构中的某个位置的情况如何。如果关联的shared_ptrs都被删除了,那么说明数据结构中隐藏了一个无效的指针。
  • 如果您知道 eventHorizo​​n 将获得指针的所有权,并最终将其删除,并且您需要为自己保留所有权 - 然后传递一个新副本,如第一种形式我展示了(helpfulContainer.eventHorizon(new A(sa1));)。
  • @John Berryman:也许我误解了你的评论。对于您的第一句话:如果 eventHorizo​​n 持有指针,并在空闲时将其删除,并且您仍然需要访问同一对象,那么您的建议可能会有所帮助。但是您需要非常彻底地重构您的代码,以便除非通过 SharedA 对象,否则永远不会使用指向原始 A 对象的指针。任何将两者混为一谈的尝试都会导致许多哀号和咬牙切齿。我认为您还需要一个用于 SharedA(const SharedA&) 的专用复制构造函数,它复制 mImpl
  • @John Berryman:你的第二句话:我不明白你的意思。如果真的, all 的 shared_ptr 被删除(在容器内部和外部) - 我假设您的意思是“销毁”而不是“删除”,因为 boost::shared_ptr 是object - 那么你在任何地方都没有任何无效的指针。除非你混合了指向同一个对象的原始指针和共享指针......但你永远不想这样做。
【解决方案3】:

因此,您正在创建一个可以删除的替身 (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)); 
}

【讨论】:

    【解决方案4】:

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

    在我看来,HelpfulContainer 没什么用,应该修复或放弃。

    如果这不可能,那么最好的方法可能是复制您要传入的 A 并将副本传递给容器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多