这是一个非常基本的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
并返回构造的对象?还有其他选择吗?
答案 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正是您要找的。 p>
希望这有帮助!
答案 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,因此整个模式大大简化。