我有一个不寻常的情况,
假设我有一个如下所示的类,
template <typename T>
class C
{
public :
C (int size) : value_(size), some_other_member_(size) {}
T &value () {return value_;}
const T &value() const {return value_;}
private :
T value_;
SomeOtherType some_other_member_;
};
该类的设计使得客户端可以完全访问成员value_
,就像std::vector
的operator[]
将返回一个引用,此类还必须通过返回引用为客户端提供完全访问权限。 setter/getter 对不行。
但是,与 std::vector
不同,我不想让客户端能够完全替换成员。也就是说,客户端应该能够调用 value_
的 const
或非 const
成员,但不允许以下情况,
C<SomeType> c(10);
SomeType another_value(5);
c.value() = another_value; // This shall not be allowed
有什么可能的方法可以让客户端完全访问value_
。从某种意义上说,类C
应该像一个容器,但是一旦它包含的东西被初始化(通过构造函数,对T
有要求,但这不是相关),客户端只能通过T
的成员函数修改value_
,而不能通过赋值替换value_
。
但是,要求 T
不可复制对我来说不是一个选择。因为C
类是可以复制的。问题的核心是,正如在类 C
中看到的那样,C
有一些成员,它们都有一个 size
属性,构造时,它们都是用相同的大小
构造的,如果允许通过赋值替换value_
,那么它就允许数据结构被破坏,因为成员可能会被破坏不再具有相同的 size
属性。
要求 T
仅在大小相同时才允许复制或赋值也不是一个选项。因为,当复制 C
对象时,源和目标之间的大小可能不同。例如,
C c1(10);
C c2(20);
c1 = c2;
完全合理。 c1
的大小已更改,但其所有成员也更改为相同的新大小,因此没问题。
我希望我已经把问题说清楚了。我总结一下,我希望 C
对 T
没有太多限制,T
基本上可以是具有所需构造函数的任何类型。 T
可以被复制和赋值。我唯一不希望客户端做的事情是通过 C::value()
分配给 value_
。
最佳答案
如果您希望用户能够调用对象上的非常量成员函数,并且希望返回对实际对象的引用,则不能完全禁止赋值,因为赋值运算符基本上就是 (您可以将 a = b
重写为 a.operator=(b)
)。因此,您要么只需要返回对对象的 const 引用,要么将包含的对象设置为 non_copyable
或者接受它可以被分配的事实。
我个人建议重新考虑设计。即使你可以禁止赋值,也不能保证该对象没有成员函数,这基本上与你的想法相同( .swap(...)
是一个典型的候选者),所以只要满足以下条件你就没有真正赢得任何东西您允许调用非常量成员函数。
但是,如果您只关心禁止意外分配,则可能会使进行此类分配变得更加困难。如果您T
不是内置的,您可以创建一个派生类,它不会公开公共(public)赋值运算符并返回对此的引用:
template <typename T>
class C{
public :
class Derived: public T {
private:
Derived(int size):T(size) {}
Derived& operator=(const Derived&) = default; //used C++11 syntax for brevity, for C++03 code it has to be implemented here
Derived(const Derived&) = default; //don't want this class to be copyied outside of C
~Derived() = default;
friend class C;
};
C (int size) : value_(size), some_other_member_(size) {}
Derived& value () {return value_;}
const Derived& value() const {return value_;}
private :
Derived value_;
SomeOtherType some_other_member_;
};
这将允许通过继承访问所有公共(public)成员,但隐藏赋值运算符(和构造函数)。当然,如果您使用 c++11,可以通过使用/定义移动构造函数/赋值并使用完美转发来增强此代码,以允许不同的构造函数。请注意,仍然可以使用 static_cast<T&>(C.value()) = foo
来分配 Derived 的 T 部分;
为了支持您无法派生的类型(内置函数...),您需要创建一个代理,该代理公开除赋值之外的所有功能。
关于C++ 防止复制成员数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13787786/