问题描述
|
我有一个其所有默认构造函数都超出我控制范围的类。我需要避免在某些情况下而不是在其他情况下构造它们。有什么我可以做的吗?
模式是这样的:
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
};
};