2012-12-09 112 views
2

我有一个不寻常的情况,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不同,我不想让客户端能够完全替换成员。也就是说,客户端应能够调用const或非的value_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的大小已更改,但其所有成员也都更改为相同的新大小,因此它是可以的。

我希望我已经说清楚了这个问题。我总结,我想要C不会对T构成太大的限制,T基本上可以是任何带有必需构造函数的类型。可以复制并分配T。我不希望客户做的唯一的细点是value_C::value()

+0

如果您为该值提供非常量引用,则可以对其进行修改。所以不要提供。一个const引用就足以读取该值。 –

回答

3

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

就我个人而言,我会建议重新考虑设计。即使你可以不允许分配,但实际上并不能保证该对象没有成员函数,它基本上是一样的想法(.swap(...)是一个典型的候选者),所以只要你允许,你还没有真正赢得任何东西调用非常量成员函数。

但是,如果您只关心禁止商业机密任务,那么您可以更难做出这样的任务。如果您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;

要支持不能派生自的类型(builtins ...),您需要创建一个代理,该代理将公开除分配以外的所有功能。

+0

感谢您的答案。这非常棒。虽然还有一些细节我可能需要考虑,但这是一个很好的策略,我可以使用(内部类的想法非常简洁)。重点是防止意外分配。如果用户想要使用某些成员函数显式更改内部if值,则可以自行完成。我有些道理,这是他们的目标,只要他们知道他们在做什么,他们就可以做任何他们想做的事情 –

1

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

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

返回const T&(常量引用)是完全反对像c.value() = 10情况(见例如,C++有效斯科特迈尔斯,项目23)。

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

+0

我在问题中提到这不是一个选项。因为用户将无法在不进行复制的情况下调用非常量值的成员,然后将其复制回来。这根本不是有效的,有时是不可能的。它更像是一个容器,它返回非const引用 –