C ++避免构造对象

问题描述

| 我有一个其所有认构造函数都超出我控制范围的类。我需要避免在某些情况下而不是在其他情况下构造它们。有什么我可以做的吗? 模式是这样的:
class A
{
public: 
    //ctor + dtor + methods
    A(); 
    ~A();
    // Data members beyond my control
    X1 x1;
    X2 x2;
    ...
    XN xn;
};

class B
{
public:
    B()
    {}

    // Data members
    A a;   // This instance of A is the issue
};
@H_502_2@

问题是
X1@H_502_2@
XN@H_502_2@完全超出了我的控制范围。 
A@H_502_2@类只是将
x1@H_502_2@
xn@H_502_2@打包在一起的一类。我想在
B@H_502_2@中有条件地创建
A@H_502_2@的实例。现在的问题是,
X1@H_502_2@
XN@H_502_2@认构造函数在某些情况下而不是其他情况下需要避免某些事情。因此,无论我是否构造
A@H_502_2@
B@H_502_2@都会尝试使用
A@H_502_2@认构造函数创建一个,这将反过来启动
X1@H_502_2@
XN@H_502_2@认构造函数。这是我需要避免的。

我目前正在使用宏来解决此问题,但想查看是否有更好的解决方案。
    


解决方法

您可能要使用诸如boost :: optional之类的Nullable类型。看起来像:
class B {
public:
    B()
    {
        /*
         * Here,a has been default constructed and is empty
         * You can do computations here and then...
         */
        if(/* some elaborate condition*/) {
            a = A();
            /* access *a from here on */
        } else {
            /* anything you want */
        }
    }

private:
    boost::optional<A> a;
};
这回答了问题,但是我认为,如果您说出您真正想要实现的目标,则可以给出更合适的答案。我觉得这更多是设计问题,而不是语言问题。随后出现了更多的想法。 在上面的“解决方案”中,
else
子句中包含什么内容?由于
A
显然只能是默认构造的,因此您不可以放置其他构造函数调用。但是,如果不初始化
a
,则会引入(环)复杂性,因为这将意味着每种方法都必须检查
a
是否处于活动状态。或者你可以扔;但是,我将重构在函数中执行检查(或其他操作)的代码;私有静态方法或匿名/静态独立式函数,如下所示:
namespace {
A
prepare_A()
{
    /* elaborate computations,possibly throw */
    return A();
    /*
     * if A had different constructors we could also conditionally
     * return an A(0) or whatever.
     */
}
} // namespace

B::B()
:
    a(prepare_A())
{
    /*
     * a is of type A,not boost::optional<A>
     */
}
但是,这假定“ 3”是可复制的。如果不是这种情况,或者不愿意复制
A
,则我认为第一个解决方案是可以接受的,条件是作为ѭ不变类
a
的一部分永远不能为空。 如果我们知道
A
B
之间的关系,推荐一些东西会更容易。如果要有条件地初始化
A
成员,为什么还要在
B
中放置它呢?也许两者之间的首选关系不应该是聚合,而应该是关联:指向
A
的指针成员(或参考成员,如果您对赋值运算符特别注意的话),例如:
class B {
public:
    explicit
    B(A& a_)
    /*
     * Important: not A const&,* we only want lvalues.
     * Contract on the caller: a must remain valid during
     * the lifetime of *this.
     */
    :
        a(&a_)
    {
        /*
         * class invariants: a != 0,* *a remains valid for the lifetime of B
         */
    }

private:
    A* a;
};
这样,您就不会引入圈数复杂性,也不必在乎如何以及何时构造A。合同给调用者增加了一些负担,但是由于我们只能将左值传递给构造函数,因此很难滥用。     ,正如Konrad Rudolph指出的那样:
class B
{
public:
  B(bool create = false)
  {
    a = (create ? new A : NULL);
  }
  ~B()
  {
    delete a;
  }
private:
  A* a;
};
    ,尚不清楚要避免使用哪个构造函数,但无论哪种情况,都可以使用联合。 从Stroustrup第4版:   如果某个联合具有一个成员,该成员具有用户定义的构造函数,复制操作,移动操作或析构函数,则将删除该联合的特殊功能(第3.3.4节,第17.6.4节);也就是说,它不能用于联合类型的对象。 因此,如果您不想在A在B中时构造它,请使用:
class B {
public:
    B()
    {}

    // Data members
    union {
       A a;   // This instance of A is the issue
    };
};