[英]Sharing object by reference or pointer
假设我有一个具有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比传递原始指针更好的解决方案,但我提出的解决方案可以被认为是坏的,但仍然是解决方案。
如果B是可选的,那么它可以表示为指针。 如果需要B,则应将其表示为参考。
如果可能的话,尝试使用初始化方法避免“两阶段构造”。 如果无法在A内部进行,则需要将B视为可选,因此将其存储为指针并测试您可能想要使用它的位置。
如果您的初始化方法(或理想情况下,构造函数)需要B,那么您应该将其作为参考传递。
这一切都假设你知道谁拥有B; 也许B拥有A的实例并通过对它自己的引用来初始化它们,或者B也拥有也拥有引用这个B实例的所有A实例的东西。
如果A拥有B的对象,那么你应该使用类似boost :: shared_ptr的东西来明确共享所有权; 假设B是动态分配的。
我会留着指针。 这里引用只是发送错误的消息。
在计划将指针指向对象并保留或共享它等情况下,不要使用对象的引用.C ++中引用的主要原因是允许运算符重载和复制构造函数等用户定义类型。 如果没有它们,就很难为这个功能提供与内置类型没有区别的语法。
但在这种情况下,你不是试图模仿内置类型。 您正在操作通常通过指针使用的对象,甚至通过几个不同的指针共享。 所以要明确这一点 。
至于b
为NULL,一定要使用assert(b)
(或类似的构造)来强制执行合同并停止无效程序。 (我不会抛出异常。即使你忘记了C ++中的异常问题,你是否计划在代码中捕获并处理这样的异常?)我也会在所有使用m_B
代码中添加这样的断言m_B
万一忘了叫A::Initialize()
。
使用引用确保指针不为null,可能会失败。 在大多数实现中,您可以从NULL或悬空指针进行引用,而不会引发任何错误。 只有在尝试使用此引用时,您的应用程序才会失败。 所以,如果有人不小心通过你的B *pb
等于NULL,你可以调用pa->Initialize(*pb)
并且没有任何反应。 除了pa->m_B
现在为NULL。
是否使用boost::shared_ptr
类的东西取决于您和您的内存管理策略。 这是一个完全不相关的问题。
传递B作为参考说明B的寿命比A的寿命(或去初始化的时间)长。如果是这种情况,你应该通过rerefence。 在内部,您还可以将引用存储在Boost引用包装器中 (但这是Boost,因此可能不是一个选项)。
如果您传递指针,并且您确定,如果您的程序正确则它们永远不应该为NULL,那么使用断言而不是if子句来检查它。
我有时也会使用你所提出的szenario,并且通常使用你提出的解决方案。 这是最简单的一个。
根据类的复杂性和设计,您还可以使用状态模式的变体,其中描述“初始化”状态的状态对象在initialize-method中构造。 在这种情况下,您可以将引用传递给state-object的构造函数。 这可能是过度的(C ++中的状态模式具有相当大的锅炉板,所以确保它值得)并且需要大量的重构,但也许它可以在某种程度上帮助你。
我会使用boost :: shared_ptr。 如果使用引用,则可能需要担心引用的范围,如果使用普通指针,则会遇到内存管理问题。 谁会删除B?
也许只是再次反思你的方案:你真的需要共享对象还是类型B的副本? 如果B将被其他类更改,并且其他类需要知道这些更改,则需要shared_ptr。
我相信你应该使用构造函数而不是使用Initialize方法。
将B传递到A的c-tor并不总是一个好的选择。 还有其他参数传递给A,这些参数在A创建时不存在。 因此,我不喜欢将部分参数传递给A-c-tor,其余参数传递给A :: Initialize()。
你听说过施工前的吗?
以下是您可以做的事情的示例:
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);
}
};
如果可能的话,我肯定会避免使用引用作为类的成员。 有一百万种方法可以搞砸你。 如果你这样做,你应该使用初始化列表,但这是你应该避免的另一个功能,如瘟疫。 它适用于简单的情况,但它可能会导致严重的问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.