C++ 中的相互 class 引用
Mutual class references in C++
我公司有一些代码采用以下形式:
class ObjectA {
public:
ObjectA(ObjectB &objectB): objectB(objectB) { }
void Initialize() { ... }
private:
ObjectB &objectB;
};
class ObjectB {
public:
ObjectB(ObjectA &objectA): objectA(objectA) { }
void Initialize() { ... }
private:
ObjectA &objectA;
};
基本上,两个 class 相互依赖。
这本身并不是困扰我的问题,(尽管 IMO 的设计不是很好),相互依赖是通过构造函数传递的两种情况下的参考.
因此,实际构建这对对象的唯一方法(它们在应用程序中都是必需的) 是传递一个不完整的引用作为依赖项。为了解决这个问题,将单独的 Initialize
方法添加为第二阶段构造函数,然后在创建两个对象后调用它们。因此,构造函数通常除了将构造函数参数分配给对象内部并在 Intizialize
方法中初始化所有内容外什么都不做。
尽管代码可能有效,但我倾向于认为这是一个根本性的缺陷设计,原因如下:
- 依赖对象的状态不能在
构造函数
- 需要开发者额外调用
Initialize
- 它排除了使用检查成员变量是否已初始化的编译器警告
- 没有什么可以阻止
Initialize
被多次调用,这可能会导致奇怪的、难以追踪的错误
我的同事告诉我,使用单独的初始化方法可以简化对象构造,因为开发人员不必担心在应用程序根目录中声明了哪些顺序对象。它们可以以完全任意的顺序声明和构造,因为一旦调用 Initialize 一切都保证有效。
我一直提倡对象构造应该遵循依赖图的相同顺序,并且应该重新设计这些对象以消除相互依赖,但我想看看 SO 专家对此有何评论。我认为这是不好的做法是错误的吗?
编辑:
这些 classes 实际构建的方式是通过工厂 class 如下所示:
Factory {
public:
Factory():
objectA(objectB),
objectB(objectA) {
}
private:
ObjectA objectA;
ObjectB objectB;
};
是的,这是不好的做法。传递对 objectB
的引用以供 objectA
使用,但它还没有正确初始化,这是一个禁忌。
它现在可能是符合标准且安全的代码,因为 ObjectA
不会尝试在其构造函数中访问传递给 ObjectB
的引用,但安全性被线程挂起。如果稍后有人决定只在构造函数中初始化或访问 objectB
中的任何内容,或以任何其他方式更改它以使用 objectB
,你最终会得到 UB。
这听起来像是在两个构造函数都具有 运行.
之后重新安置指针的用例
我也不喜欢发布的代码 - 它不必要地复杂和脆弱。当两个 类 像这样合作时通常会有一些所有权的概念,所以,如果类型 A
的对象拥有类型 B
的对象(他们可能应该),那么你会做一些事情喜欢:
#include <memory>
class A;
class B
{
public:
B (A& a) : a (a) { }
private:
A& a;
};
class A
{
public:
A () { b = std::make_unique <B> (*this); }
private:
std::unique_ptr <B> b;
};
我公司有一些代码采用以下形式:
class ObjectA {
public:
ObjectA(ObjectB &objectB): objectB(objectB) { }
void Initialize() { ... }
private:
ObjectB &objectB;
};
class ObjectB {
public:
ObjectB(ObjectA &objectA): objectA(objectA) { }
void Initialize() { ... }
private:
ObjectA &objectA;
};
基本上,两个 class 相互依赖。
这本身并不是困扰我的问题,(尽管 IMO 的设计不是很好),相互依赖是通过构造函数传递的两种情况下的参考.
因此,实际构建这对对象的唯一方法(它们在应用程序中都是必需的) 是传递一个不完整的引用作为依赖项。为了解决这个问题,将单独的 Initialize
方法添加为第二阶段构造函数,然后在创建两个对象后调用它们。因此,构造函数通常除了将构造函数参数分配给对象内部并在 Intizialize
方法中初始化所有内容外什么都不做。
尽管代码可能有效,但我倾向于认为这是一个根本性的缺陷设计,原因如下:
- 依赖对象的状态不能在 构造函数
- 需要开发者额外调用
Initialize
- 它排除了使用检查成员变量是否已初始化的编译器警告
- 没有什么可以阻止
Initialize
被多次调用,这可能会导致奇怪的、难以追踪的错误
我的同事告诉我,使用单独的初始化方法可以简化对象构造,因为开发人员不必担心在应用程序根目录中声明了哪些顺序对象。它们可以以完全任意的顺序声明和构造,因为一旦调用 Initialize 一切都保证有效。
我一直提倡对象构造应该遵循依赖图的相同顺序,并且应该重新设计这些对象以消除相互依赖,但我想看看 SO 专家对此有何评论。我认为这是不好的做法是错误的吗?
编辑:
这些 classes 实际构建的方式是通过工厂 class 如下所示:
Factory {
public:
Factory():
objectA(objectB),
objectB(objectA) {
}
private:
ObjectA objectA;
ObjectB objectB;
};
是的,这是不好的做法。传递对 objectB
的引用以供 objectA
使用,但它还没有正确初始化,这是一个禁忌。
它现在可能是符合标准且安全的代码,因为 ObjectA
不会尝试在其构造函数中访问传递给 ObjectB
的引用,但安全性被线程挂起。如果稍后有人决定只在构造函数中初始化或访问 objectB
中的任何内容,或以任何其他方式更改它以使用 objectB
,你最终会得到 UB。
这听起来像是在两个构造函数都具有 运行.
之后重新安置指针的用例我也不喜欢发布的代码 - 它不必要地复杂和脆弱。当两个 类 像这样合作时通常会有一些所有权的概念,所以,如果类型 A
的对象拥有类型 B
的对象(他们可能应该),那么你会做一些事情喜欢:
#include <memory>
class A;
class B
{
public:
B (A& a) : a (a) { }
private:
A& a;
};
class A
{
public:
A () { b = std::make_unique <B> (*this); }
private:
std::unique_ptr <B> b;
};