C ++防止复制成员数据

时间:2012-12-09 13:26:38

标签: 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::vector的{​​{1}}将返回引用一样,此类也必须通过返回引用为客户端提供完全访问权限。 setter / getter对不会这样做。

但是,与operator[]不同,我不希望客户端能够完全替换成员。也就是说,客户应该可以致电std::vector成员constconst成员,但不允许以下内容,

value_

我是否可以通过任何方式向客户授予对 C<SomeType> c(10); SomeType another_value(5); c.value() = another_value; // This shall not be allowed 的完全访问权限。在某种意义上,类value_应该像一个容器,但是一旦它包含的东西被初始化(通过构造函数,有C的要求,但这里没有相关性),客户端可以只修改Tvalue_的成员函数,而不是通过赋值替换T

但是,要求value_不可复制,对我来说不是一个选项。因为可以复制类T。问题的核心是,如同类C中所见,C有几个成员,它们都有一个C属性,在构造时,它们都是用相同的size,如果允许通过赋值替换size,那么它允许数据结构在成员可能不再具有相同的value_属性的情况下被破坏。

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

C

非常合理。 C c1(10); C c2(20); c1 = c2; 的大小已更改,但其所有成员也更改为相同的新大小,因此可以。

我希望我已经明确说明了这个问题。我总结一下,我希望c1C没有太多限制,T基本上可以是任何具有必需构造函数的类型。可以复制和分配T。我不希望客户做的唯一的事情就是通过T分配到value_

2 个答案:

答案 0 :(得分:3)

如果希望用户能够在对象上调用非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,这个代码可以通过使用/定义移动构造函数/赋值和使用完美转发来增强,以允许不同的构造函数。请注意,Derived的T部分仍然可以使用static_cast<T&>(C.value()) = foo;

分配

要支持您无法从(builtins ...)派生的类型,您需要创建一个代理,它会公开除分配之外的所有功能。

答案 1 :(得分:1)

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

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

返回const T&(const-reference)完全违反c.value() = 10之类的情况(参见Scott Meyers的有效C ++,第23项)。

我认为这也解决了复制问题:您的课程仍然可以复制。