假设我有一个具有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:
代码不是新的,我只是想“修复”一些问题。 实际上我不是在谈论一些具体的A和B类,而是在我的代码库中找到问题的方法。代码广泛传递指向B的指针并在Initialize()中验证它是否为NULL。
将B传递给A的c-tor并不总是一个好选择。还有其他参数传递给A,这些参数在A创建时不存在。因此,我不喜欢将部分参数传递给A-c-tor,其余参数传递给A :: Initialize()。
shared_ptr也可以是“NULL”,所以将它传递给A :: Initialize()与仅传递指向B的方法没有区别,在这方面,如果B是强制性的,则Initialize()的签名不会声明或不。在我的情况下它是,我想通过传递参考B来表达它。
我们的代码目前根本没有使用boost。因此,虽然shared_ptr比仅传递原始指针更好的解决方案,但我提出的解决方案可以被认为是坏的,但仍然是解决方案。
答案 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)
如果可能的话,我肯定会避免使用引用作为类的成员。有一百万种方法可以搞砸你。如果你这样做,你应该使用初始化列表,但这是你应该避免的另一个功能,如瘟疫。它适用于简单的情况,但它可能会导致严重的问题。