C++ 阻止复制成员数据



我有一个不寻常的情况,

假设我有一堂像下面的课,

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::vectoroperator[]会返回一个引用一样,这个类也必须通过返回一个引用来赋予客户端完全访问权限。二传手/吸气手对是不行的。

但是,与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属性,当构造时,它们都是用相同的size构造的,如果允许通过赋值替换value_,那么它允许数据结构被破坏,因为成员可能不再具有相同的size属性。

要求T仅在大小相同时才允许复制或分配也不是一种选择。因为,在复制C对象时,源对象和目标对象之间的大小可能不同。例如

C c1(10);
C c2(20);
c1 = c2;

是完全合理的。c1的大小已更改,但其所有成员也更改为相同的新大小,因此可以。

我希望我已经把问题说清楚了。我总结,我希望CT没有太大的限制,T基本上可以是任何具有所需构造函数的类型。 可以复制和分配T。我不希望客户做的唯一瘦是分配给value_通过C::value()

如果您希望用户能够在对象上调用非 const 成员函数,并且想要返回对实际对象的引用,则不能完全禁止赋值,因为赋值运算符基本上就是这样(您可以将a = b重写为a.operator=(b))。因此,您要么只需要返回对对象的 const 引用,要么使包含的对象non_copyable,要么接受可以分配给它的事实。

就个人而言,我建议重新考虑设计。即使你可以禁止赋值,也不能保证对象没有成员函数,它做的基本思考(.swap(...)是一个典型的候选者),所以只要你允许调用非 const 成员函数,你就不会真正赢得任何东西。

但是,如果您只关心不允许意外分配,则可以使此类分配更加困难。如果T不是内置的,则可以创建一个派生类,该类不会公开公共赋值运算符并返回对该运算符的引用:

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_;
};

这将通过继承为所有公共成员提供访问权限,但隐藏赋值运算符(和构造函数)。当然,如果您使用 c++11,则可以通过使用/定义移动构造函数/赋值和使用完美转发来增强此代码,以允许不同的构造函数。注意,派生的 T 部分仍然可以分配给使用static_cast<T&>(C.value()) = foo;

为了支持无法派生的类型(内置...),您需要创建一个代理,该代理公开除赋值之外的所有功能。

至于你的getter/setter问题,我会写

const T& value() const; // as getter
void value(const T&); // as setter

返回const T&(const-reference)完全反对c.value() = 10等情况(参见例如。斯科特·迈耶斯的有效C++,第23项)。

我认为这也解决了复制问题:你的类仍然是可复制的。

最新更新