如何提供许多构造函数,但没有太多依赖项?

时间:2010-02-06 21:28:53

标签: c++ design-patterns constructor

这是一个非常基本的C ++设计问题:

我有一个类,它包含一些构造对象后只读的数据:

class Foo {
private:
  class Impl;
  Impl* impl_;  
public:
  int get(int i); // access internal data elements
};

现在,我想实现几种方法来构造一个Foo对象并用数据填充它:来自std::istream,来自迭代器,向量等。什么是最好的实现方式是什么?

我可以直接在Foo中添加所有这些构造函数,但我真的不希望Foo用户必须包含std::istream等。我也担心类包含太多代码。

最常用的方法是什么?我想,添加一些私有addElement函数,然后定义通过读取数据创建Foo对象的友元工厂函数,调用addElement并返回构造的对象?还有其他选择吗?

6 个答案:

答案 0 :(得分:10)

如果你想构建一个范围内的东西,可能是:

class X
{
public:
    template <class InputIterator>
    X(InputIterator first, InputIterator last);
};

用法:

//from array
X a(array, array + array_size);

//from vector
X b(vec.begin(), vec.end());

//from stream
X c((std::istream_iterator<Y>(std::cin)), std::istream_iterator<Y>());

答案 1 :(得分:1)

  

我可以添加所有这些   建设者直接在Foo,但我   真的不想让Foo用户拥有   包括std :: istream等我也是   担心课程也包含在内   很多代码;用户可能想要创建   很多Foo对象,但我不想要   每个对象包含很多   建筑规范。

std :: istream的构造函数可以转发声明它并将其作为引用传递。这样,您的用户不需要包含istream来包含您的Foo.h,但您需要在Foo.cpp中包含istream。

我不确定我理解你的第二个异议。对象不携带代码,只包含数据。假设我们不讨论模板,代码只存在一次。如果程序没有使用构造函数,那么链接器应该将其剥离。提供许多建设者应该没有浪费。

答案 2 :(得分:1)

  

我可以添加所有这些   建设者直接在Foo,但我   真的不想让Foo用户拥有   包括std :: istream等。

如果使用istream构造它们,则必须在实际使用istream类的文件中使用相关的头文件。

  

我也担心上课   包含太多代码;用户可以   想要创建许多Foo对象,但我   不希望每个对象都包含很多   建筑规范。

你好像很困惑。每个对象都不包含代码的副本 - 只有一个副本。

答案 3 :(得分:1)

有一个简单的解决方案:使用另一个类作为中间人。

struct FooBuild
{
  // attributes of Foo
};

class Foo
{
public:
  Foo(const FooBuild&);

private:
  // attributes of Foo, some of them const
};

然后任何人都可以轻松地设置FooBuild,并从中构建Foo对象。这样您就不必提供太多的构造函数,并且您仍然可以轻松地为Foo维护一个不变的类,并且像往常一样在构造函数中进行第一次验证。

我从python和它的frozenset类中接受了这个想法:)

答案 4 :(得分:0)

Builder Pattern正是您要找的。

希望这有帮助!

答案 5 :(得分:0)

您正在谈论两种不同的代码,1)源代码大小(仅影响构建时间)和2)可执行大小(“编译代码”大小或代码段)。它们是相关的,但绝对不一样。

第一个是在C ++中要解决的难题,因为该语言需要单片独立的TU。 (与Go之类的语言相比,设计师学会了从C语言中学习避免这个问题。)Templates帮助,就像你只是使用forward declarations(“非定义的声明”)一样不需要定义。 (虽然模板很有帮助,因为它们需要在头文件中实现所有代码。)在当前的C ++中,大多数构建时间都是deal with

可以使用每个ctor可以调用的公共初始化方法或公共基础来缓解第二个问题。后者具有其他优点,例如当成员的初始化可以或必须在初始化列表中完成时。例如:

struct SpanBase {
  SpanBase(int start, int stop, int step)
  : start(start), stop(stop), step(step)
  // any complex code in the init list would normally be duplicated
  // in each Span ctor
  {
    IMAGINE("complex code executed by each Span ctor");
    if (start > stop) throw std::logic_error("Span: start exceeds stop");
  }
protected:
  int start, stop, step;
};

struct Span : protected SpanBase {
  // Protected inheritance lets any class derived from Span access members
  // which would be protected in Span if SpanBase didn't exist.  If Span
  // does not have any, then private inheritance can be used.

  Span(int stop) : SpanBase(0, stop, 1) {}
  Span(int start, int stop) : SpanBase(start, stop, 1) {}

  Span(int start, int stop, int step): StepBase(start, stop, step) {}
  // this one could be handled by a default value, but that's not always true
};

最后,C ++ 0x允许您从一个ctor委托给另一个ctor,因此整个模式大大简化。