我正在尝试为我的软件定义一个好的设计,这意味着要小心对某些变量的读/写访问。这里我简化了讨论的程序。希望这对其他人也有帮助。 :-)
假设我们有一个类 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 的共享指针 ( XPtr
) 自动转换为其常量版本 ( 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()
,这是常量,我可以调用joo()
这将修改 xs
的数据。不是我想要的。在这里我更喜欢编译错误。
完整代码可在 ideone.com 获得.
问题是:是否可以在不将 vector 复制到其 const 版本的情况下执行相同的操作?或者,更一般地说,是否有既高效又易于理解的好技术/模式?
谢谢。 :-)
最佳答案
我认为通常的答案是对于类模板 X<T>
, 任何 X<const T>
可以专门化,因此不允许编译器简单地假设它可以转换 X<T>
的指针或引用至 X<const T>
并且没有通用的方式来表达这两个实际上是可转换的。但后来我想:等等,有一种方法可以说 X<T>
是一个 X<const T>
. IS A 通过继承来表达。
虽然这对 std::shared_ptr
没有帮助或标准容器,这是您在实现自己的类时可能希望使用的一种技术。事实上,我想知道是否std::shared_ptr
并且可以/应该改进容器以支持这一点。谁能看出这有什么问题吗?
我想到的技术会像这样工作:
template< typename T > struct my_ptr : my_ptr< const T >
{
using my_ptr< const T >::my_ptr;
T& operator*() const { return *this->p_; }
};
template< typename T > struct my_ptr< const T >
{
protected:
T* p_;
public:
explicit my_ptr( T* p )
: p_(p)
{
}
// just to test nothing is copied
my_ptr( const my_ptr& p ) = delete;
~my_ptr()
{
delete p_;
}
const T& operator*() const { return *p_; }
};
关于c++ - shared_ptr<T> 到 shared_ptr<T const> 和 vector<T> 到 vector<T const>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19616586/