通过引用或指针共享对象

时间:2009-12-06 17:29:23

标签: c++

假设我有一个具有Initialize()方法的A类对象。 该方法接收对象B,其被保持为对象数据成员。 B对象在多个对象之间共享,因此A应包含最初接收的B对象而不是其副本。

   class A 
   {
       public:
          bool Initialize(B?? b);
       private:
          B?? m_B;    
    }

对象B必须出现。因此,我认为通过引用传递它,而不是通过指针传递,并在B为NULL的情况下失败Initialize()。

m_B的唯一替代是指针B的类型(它不能作为B的引用,因为B的初始化不是在A c-tor中完成的)。因此,Initialize应该如下所示:

bool A::Initialize(B& b) 
{
    m_B = &b;
    ....
}

这种方法可以吗?

UPD:

  1. 代码不是新的,我只是想“修复”一些问题。 实际上我不是在谈论一些具体的A和B类,而是在我的代码库中找到问题的方法。代码广泛传递指向B的指针并在Initialize()中验证它是否为NULL。

  2. 将B传递给A的c-tor并不总是一个好选择。还有其他参数传递给A,这些参数在A创建时不存在。因此,我不喜欢将部分参数传递给A-c-tor,其余参数传递给A :: Initialize()。

  3. shared_ptr也可以是“NULL”,所以将它传递给A :: Initialize()与仅传递指向B的方法没有区别,在这方面,如果B是强制性的,则Initialize()的签名不会声明或不。在我的情况下它是,我想通过传递参考B来表达它。

  4. 我们的代码目前根本没有使用boost。因此,虽然shared_ptr比仅传递原始指针更好的解决方案,但我提出的解决方案可以被认为是坏的,但仍然是解决方案。

6 个答案:

答案 0 :(得分:2)

如果B是可选的,那么它可以表示为指针。如果需要B,则应将其表示为参考。

如果可能,请尝试使用初始化方法避免“两阶段构建”。如果无法在A内部进行,则需要将B视为可选,因此将其存储为指针并测试您可能想要使用它的位置。

如果您的初始化方法(或理想情况下,构造函数)需要B,那么您应该将其作为参考传递。

这一切都假定你知道谁拥有B;也许B拥有A的实例并使用对它自己的引用来初始化它们,或者B也拥有也拥有引用这个B实例的所有A实例的东西。

如果A拥有B的对象,那么你应该使用类似boost :: shared_ptr的东西来明确共享所有权;假设B动态分配了新的。

答案 1 :(得分:1)

我会留在指针上。 这里的参考只是发送错误信息。

当您计划将指针指向对象时,不要使用对象的引用 并保留或分享等 C ++中引用的主要原因是允许运算符重载等内容 并复制构造函数以用于用户定义的类型。 没有它们,很难用语法提供这种功能 与内置类型没有区别。

但在这种情况下,你不是试图模仿内置类型。 您正在对通常通过指针使用的对象进行操作 甚至通过几个不同的指针共享。 所以要明确

至于b为NULL,一定要使用assert(b)(或类似的构造) 执行合同并停止无效程序。 (我不会抛出异常。 即使你忘记了C ++中的异常问题, 你打算在你的代码中捕获和处理这样的异常吗?) 我还要为使用m_B的所有代码添加此类断言 如果有人忘记拨打A::Initialize()

使用引用确保指针不为null,可能会失败。 在大多数实现中,您可以从NULL或悬空指针进行引用 没有引起任何错误。 只有在尝试使用此引用时,您的应用程序才会失败。 所以如果有人不小心通过你B *pb等于NULL, 你可以打电话给pa->Initialize(*pb)但没有任何反应。 除了pa->m_B现在为NULL。

是否使用boost::shared_ptr之类的东西取决于您和您的记忆策略 管理。 这是一个完全不相关的问题。

答案 2 :(得分:1)

传递B作为参考说明B的寿命比A的寿命(或去初始化的时间)长。如果是这种情况,你应该通过rerefence传递。在内部,你也可以将引用存储在Boost reference wrapper中(但这是Boost,所以可能不是一个选项)。

如果您传递指针,并且您确定,如果您的程序正确则它们永远不应为NULL,那么请使用assertions而不是if子句来检查它。

我有时也会使用你所建议的szenario,并且通常会使用你提出的解决方案。这是最简单的一个。

根据类的复杂性和设计,您还可以使用state pattern的变体,其中描述“初始化”状态的状态对象在initialize-method中构造。在这种情况下,您可以将引用传递给state-object的构造函数。这可能是过度的(C ++中的状态模式具有相当大的底板,所以确保它值得)并且需要大量的重构,但也许它可以在某种程度上帮助你。

答案 3 :(得分:0)

我会使用boost :: shared_ptr。如果使用引用,则可能需要担心引用的范围,如果使用普通指针,则会遇到内存管理问题。谁会删除B?

也许只是再次反思你的方案:你真的需要分享这个对象还是B型的副本?如果B将被其他类更改,并且其他类需要知道这些更改,则需要shared_ptr。

答案 4 :(得分:0)

我相信你应该使用构造函数而不是使用Initialize方法。

  

将B传递给A的c-tor并不总是如此   也是不错的选择。还有其他   参数传递给A,但不是   存在于创建时。所以我   woudl不喜欢通过部分   参数为A c-tor,其余为   A ::初始化()。

你听说过预建吗?

以下是您可以做的事情的示例:

class IsEmpty{};
class A
{
    B *b_;
    int *c_;
    char *d_;

    void Initialize(B *b)
    {
        b_ = b;
        c_ = b_->Getc();
        d_ = b_->Getd();
    }

public:
    A(B *b)
    : b_(0), c_(0), d_(0)
    {
        if(b == 0) throw IsEmpty();   
        Initialize(b);
    }
};

答案 5 :(得分:0)

如果可能的话,我肯定会避免使用引用作为类的成员。有一百万种方法可以搞砸你。如果你这样做,你应该使用初始化列表,但这是你应该避免的另一个功能,如瘟疫。它适用于简单的情况,但它可能会导致严重的问题。