【问题标题】:Better to instantiate object with correct values and then push to vector, or instantiate, push to vector then set correct values?最好用正确的值实例化对象然后推送到向量,或者实例化,推送到向量然后设置正确的值?
【发布时间】:2014-11-28 21:02:36
【问题描述】:

假设我有这样的课程:

class Foo{
    Foo* otherFooObj;
    Foo* otherFooObj2;
.....
};

最好:

A.

  1. 使用函数获取成员变量的正确值(otherFooObjotherFooObj2 的地址等)
  2. 用这些正确的值实例化(构造)一个 Foo 对象。
  3. 将对象推送到矢量。

乙。

  1. 使用虚拟值(otherFooObjotherFooObj2 的虚拟地址)实例化 Foo 对象
  2. 将对象推送到矢量。
  3. 使用函数将 Foo 对象的成员变量设置为正确的值,一旦它位于向量内。

C.

  1. 用虚拟值实例化一个 Foo 对象
  2. 使用函数获取正确的值并将 Foo 的成员变量设置为这些值
  3. 将对象推送到矢量。

现在,我认为这些设计模式之间没有太大区别,我遵循哪种流程并不重要。但我想就此获得更多意见,并想知道是否还有其他我忽略的因素,我应该考虑(我觉得有,也许有指针等)。

【问题讨论】:

  • 或者使用emplace_back
  • 你到底在烦恼什么?可读性?懒惰写作?
  • @Dieter 你是说它会比上面三个选项更好吗?
  • 可能是很好的设计模式,无论是一种“更安全”的编码方式和性能。

标签: c++ algorithm oop vector


【解决方案1】:

这个问题有点笼统,无法适当回答。一个好的设计目标是仅让对象处于 valid 状态,根据您的设计中的一些有效定义。另一种选择是不能事先相信一个对象处于有效状态,这会使您的生活变得复杂,并导致您错误地生活在一个半生不熟的对象周围。

从这个角度来看,我将为成员创建值并调用构造函数,将对象从不存在状态移动到有效状态。

在您的情况下,您似乎正在构建一个自引用数据结构:图、树或类似的东西。如果是这种情况,可能是 Foo 将两个成员设置为 nullptr 是有意义的,您可以考虑替代方案,但我仍然会提供一个构造函数,使您能够将对象设置为完整状态:

class Foo {
   Foo *a, *b;
public:
   Foo() : a(), b() {} // initialize both to nullptr, assuming this is a *valid* state
   Foo(Foo *a, Foo *b) : a(a), b(b) {}
...
};

回到最初的问题,我会选择为成员创建值并将 emplace 放入向量中:

Foo *a = f();
Foo *b = f();
vector.emplace_back(a, b);

【讨论】:

  • 是的,我正在构建一个自引用数据结构。感谢您提供这个有用的答案,介绍有效状态、半生不熟的对象并指出 emplace_back 的想法。在研究 emplace 时,我找到了这篇有用的文章:stackoverflow.com/questions/4303513/push-back-vs-emplace-back 其中提到了 emplace 避免半生不熟和无效状态的能力。很有道理。
  • @Gnuey:你并不真的需要 emplace_back,你可以创建对象然后推送它:Foo value(a,b); vector.push_back(value);(虽然它不会一样高效)
  • 啊,所以 emplace 也有一些效率奖励。这太好了,因为我肯定需要任何我能获得的额外性能提升。
  • 我喜欢这种方法(使用emplace_back),如果类的构造函数是微不足道的并且不使用这么多类似的参数并且没有指定默认值。这样做的原因是,如果您将类的构造函数更改为旧参数仍然类型兼容的东西,您可能会引入很难找到的错误。 AFAIK 甚至 Visual Assist(C++ 重构工具)都无法在 emplace_back 中定位和重构构造函数签名,所以请注意!
【解决方案2】:

避免生成无效状态。如果您没有设置成员指针,那么至少您必须将它们设置为nullptr。如果可能,尽快生成一个有用且有效的状态(除非这很昂贵,否则您可能想要使用延迟初始化,但仍然是一个有效但不完整的初始状态)。因此,如果您能获得成员的最终正确值,请立即设置它们。

如果这些指针拥有任何内存(从新分配的内存初始化),那么您必须非常小心。在这种情况下,您实际上应该使用std::unique_ptr<> 并移动您的对象,或者更好的是直接通过vector::emplace_back() 在其矢量位置构造。

另外,如果您提前知道对象的总数,您可能希望vector::reserve() 有足够的空间,然后再将对象放入向量中。

【讨论】:

  • 新分配的内存是指在堆栈、堆或两者上实例化的东西吗?这是延迟初始化的一个很好的定义吗? (只需等到最后一分钟初始化值)en.wikipedia.org/wiki/Lazy_initialization
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-04
  • 2018-02-05
  • 1970-01-01
相关资源
最近更新 更多