我正在尝试为我的软件定义一个良好的设计,这意味着需要小心处理一些变量的读写访问。这里我简化了程序以便讨论。希望这对其他人也有所帮助。 :-)
假设我们有一个如下的类X:
class X {
int x;
public:
X(int y) : x(y) { }
void print() const { std::cout << "X::" << x << std::endl; }
void foo() { ++x; }
};
假设今后这个类将被子类 X1,X2 等继承,并重新实现 print() 和 foo() 方法。(我省略了 virtual 关键字只是为了方便,因为这不是我遇到的实际问题。)
由于我们将使用多态性,让我们使用 (智能) 指针并定义一个简单工厂:
using XPtr = std::shared_ptr<X>;
using ConstXPtr = std::shared_ptr<X const>;
XPtr createX(int x) { return std::make_shared<X>(x); }
目前为止,一切都很好:我可以定义goo(p)
,它可以读写p
,并且还可以定义hoo(p)
,它只能读取p
。
void goo(XPtr p) {
p->print();
p->foo();
p->print();
}
void hoo(ConstXPtr p) {
p->print();
// p->foo(); // ERROR :-)
}
而调用站点看起来像这样:
XPtr p = createX(42);
goo(p);
hoo(p);
指向 X 的 shared pointer(XPtr)会自动转换为其 const 版本(ConstXPtr)。太好了,这正是我想要的!
现在遇到麻烦了:我需要一个 X
的异构集合。我选择使用 std::vector<XPtr>
。(也可以是 list
,为什么不呢。)
我考虑的设计如下。我有两个版本的容器:一个可读写访问其元素,一个只能以只读方式访问其元素。
using XsPtr = std::vector<XPtr>;
using ConstXsPtr = std::vector<ConstXPtr>;
我有一个处理这些数据的类:
class E {
XsPtr xs;
public:
E() {
for (auto i : { 2, 3, 5, 7, 11, 13 }) {
xs.emplace_back(createX(std::move(i)));
}
}
void loo() {
std::cout << "\n\nloo()" << std::endl;
ioo(toConst(xs));
joo(xs);
ioo(toConst(xs));
}
void moo() const {
std::cout << "\n\nmoo()" << std::endl;
ioo(toConst(xs));
joo(xs); // Should not be allowed
ioo(toConst(xs));
}
};
ioo()
和 joo()
函数如下:
void ioo(ConstXsPtr xs) {
for (auto p : xs) {
p->print();
// p->foo(); // ERROR :-)
}
}
void joo(XsPtr xs) {
for (auto p: xs) {
p->foo();
}
}
正如您所看到的,在 E::loo()
和 E::moo()
中,我需要使用 toConst()
进行一些转换:
ConstXsPtr toConst(XsPtr xs) {
ConstXsPtr cxs(xs.size());
std::copy(std::begin(xs), std::end(xs), std::begin(cxs));
return cxs;
}
但这意味着一遍又一遍的复制... :-/
还有,对于 moo()
这个被声明为 const 的函数,我却可以调用 joo()
来修改 xs
的数据。这不是我想要的。我希望在这里能够出现编译错误。
完整代码可在 ideone.com 查看。
问题是:是否可能在不将向量复制到其 const 版本的情况下完成相同的操作?或者更一般地说,是否有一种既高效又易于理解的技术/模式?
谢谢。 :-)
boost::adaptors::transformed
和合适的函数对象获取一个带有const
视图,以转换您的共享指针。 - Xeoboost::adaptors::transformed
,但似乎在某个时候我必须复制一些东西,有点像上面的代码但语法不同,对吗?如果不是这样,您能否在下面给出一个示例呢? :-) - Hiurastd::move(i)
不会移动任何东西。move
并不会移动,它只是一个转换。也许这只是因为你复制了实际移动代码的缘故 :) - typ1232