如何防止用继承的运算符[]赋值?
How to prevent value assignment with inherited operator[]?
我有一个名为 SortedArrayList<T>
的自定义结构,它根据比较器对其元素进行排序,我想防止使用 operator[]
进行分配。
示例:
ArrayList.h
template <typename T> class ArrayList : public List<T> {
virtual T& operator[](const int& index) override; //override List<T>
virtual const T operator[](const int& index) const override; //override List<T>
}
SortedLinkedList.h 与以下运算符
template <typename T> class SortedArrayList : public ArrayList<T> {
public:
SortedArrayList<T>(const std::function<bool(const T&, const T&)>& comparator);
T& operator[](const int& index) override; //get reference (LHS)
const T operator[](const int& index) const override; //get copy (RHS)
}
Test.h
ArrayList<int>* regular = new ArrayList<int>();
ArrayList<int>* sorted = new SortedArrayList<int>(cmpfn);
(*regular)[0] == 5; //allow
(*regular)[0] = 5; //allow
(*sorted)[0] == 7; //allow
(*sorted)[0] = 7; //except
这个操作可以吗?
我所说的阻止是指抛出异常或警告用户不要这样做的东西。
在我看来,这里的最佳实践是实现 at(const int& index)
方法而不是重载 []
。无论如何,这对界面的用户来说会更清楚。
在std::map
和其他std
数据结构中也有类似的功能。例如:http://www.cplusplus.com/reference/map/map/at/
- 为什么要传递索引作为参考?绝对不需要...
- 我个人建议对数组索引使用无符号整数类型(负索引到底是什么意思???)。
- const 由值返回的类型(几乎)没有意义 - 它无论如何都会被复制到另一个变量(然后 将 是可修改的),但是你阻止了移动语义。 ..
所以:
T& operator[](unsigned int index); //get reference (LHS)
T operator[](unsigned int index) const; //get copy (RHS)
(只是一些改进建议...)
现在进入实际问题:禁止修改非常简单:
//T& operator[](unsigned int index); //get reference (LHS)
T const& operator[](unsigned int index) const; //get copy (RHS)
只有一个索引运算符,总是返回 const 引用...如果用户可以接受引用,那很好,否则 he/she 无论如何都会复制值...
根据修改后的问题进行编辑:
现在涉及到继承,事情变得更加复杂。你不能只去掉一些继承的函数,继承的函数将允许元素修改。
在给定的情况下,我会考虑重新设计(如果可能的话):
class ArrayListBase
{
public:
T const& operator[](unsigned int index) const;
// either copy or const reference, whichever appears more appropriate to you...
};
class ArrayList : public ArrayListBase
{
public:
using ArrayListBase::operator[];
T& operator[](unsigned int index);
}
class SortedArrayList : public ArrayListBase
{
public:
// well, simply does not add an overload...
}
插入函数在基础 class 中可能是纯虚拟的(公共接口似乎适合),或者仅在派生的 class 中可用。决定你...
聚合优于继承:
template <typename T> class SortedArrayList {
ArrayList<T> m_the_list;
public:
SortedArrayList<T>(const std::function<bool(const T&, const T&)>& comparator);
const T& operator[](const int& index) const {return m_the_list[index];}; // always get const reference
// Can act as a *const* ArrayList<T>, but not as a mutable ArrayList<T>, as that would violate Liskov's substitution principle.
operator const ArrayList<T>&() const {return m_the_list;}
}
与 Stephen Newell correctly 一样,当您使用继承时,您可以保证您的 class SortedArrayList
在任何可能的情况下都可以充当 ArrayList
。在您的示例中显然不是这种情况。
您可以阅读更多内容here,了解违反 Liskov 替换原则是多么糟糕的想法。
你不应该这样做。这表明设计不当见 the C++ FAQ on Inheritance. Your subclass doesn't fulfill the "is-a" requirement for public inheritance if it can't be used in all ways as the base class (LSP)。
如果您想要一种类型的容器允许成员替换而另一种不允许,则定义仅允许 const 成员访问的基础 class(无需将其设为虚拟)。然后从那里分支到 MutableList 和 ImmutableList,并让 SortedArrayList 从 Immutable 列表派生。
我有一个名为 SortedArrayList<T>
的自定义结构,它根据比较器对其元素进行排序,我想防止使用 operator[]
进行分配。
示例:
ArrayList.h
template <typename T> class ArrayList : public List<T> {
virtual T& operator[](const int& index) override; //override List<T>
virtual const T operator[](const int& index) const override; //override List<T>
}
SortedLinkedList.h 与以下运算符
template <typename T> class SortedArrayList : public ArrayList<T> {
public:
SortedArrayList<T>(const std::function<bool(const T&, const T&)>& comparator);
T& operator[](const int& index) override; //get reference (LHS)
const T operator[](const int& index) const override; //get copy (RHS)
}
Test.h
ArrayList<int>* regular = new ArrayList<int>();
ArrayList<int>* sorted = new SortedArrayList<int>(cmpfn);
(*regular)[0] == 5; //allow
(*regular)[0] = 5; //allow
(*sorted)[0] == 7; //allow
(*sorted)[0] = 7; //except
这个操作可以吗?
我所说的阻止是指抛出异常或警告用户不要这样做的东西。
在我看来,这里的最佳实践是实现 at(const int& index)
方法而不是重载 []
。无论如何,这对界面的用户来说会更清楚。
在std::map
和其他std
数据结构中也有类似的功能。例如:http://www.cplusplus.com/reference/map/map/at/
- 为什么要传递索引作为参考?绝对不需要...
- 我个人建议对数组索引使用无符号整数类型(负索引到底是什么意思???)。
- const 由值返回的类型(几乎)没有意义 - 它无论如何都会被复制到另一个变量(然后 将 是可修改的),但是你阻止了移动语义。 ..
所以:
T& operator[](unsigned int index); //get reference (LHS)
T operator[](unsigned int index) const; //get copy (RHS)
(只是一些改进建议...)
现在进入实际问题:禁止修改非常简单:
//T& operator[](unsigned int index); //get reference (LHS)
T const& operator[](unsigned int index) const; //get copy (RHS)
只有一个索引运算符,总是返回 const 引用...如果用户可以接受引用,那很好,否则 he/she 无论如何都会复制值...
根据修改后的问题进行编辑:
现在涉及到继承,事情变得更加复杂。你不能只去掉一些继承的函数,继承的函数将允许元素修改。
在给定的情况下,我会考虑重新设计(如果可能的话):
class ArrayListBase
{
public:
T const& operator[](unsigned int index) const;
// either copy or const reference, whichever appears more appropriate to you...
};
class ArrayList : public ArrayListBase
{
public:
using ArrayListBase::operator[];
T& operator[](unsigned int index);
}
class SortedArrayList : public ArrayListBase
{
public:
// well, simply does not add an overload...
}
插入函数在基础 class 中可能是纯虚拟的(公共接口似乎适合),或者仅在派生的 class 中可用。决定你...
聚合优于继承:
template <typename T> class SortedArrayList {
ArrayList<T> m_the_list;
public:
SortedArrayList<T>(const std::function<bool(const T&, const T&)>& comparator);
const T& operator[](const int& index) const {return m_the_list[index];}; // always get const reference
// Can act as a *const* ArrayList<T>, but not as a mutable ArrayList<T>, as that would violate Liskov's substitution principle.
operator const ArrayList<T>&() const {return m_the_list;}
}
与 Stephen Newell correctly SortedArrayList
在任何可能的情况下都可以充当 ArrayList
。在您的示例中显然不是这种情况。
您可以阅读更多内容here,了解违反 Liskov 替换原则是多么糟糕的想法。
你不应该这样做。这表明设计不当见 the C++ FAQ on Inheritance. Your subclass doesn't fulfill the "is-a" requirement for public inheritance if it can't be used in all ways as the base class (LSP)。
如果您想要一种类型的容器允许成员替换而另一种不允许,则定义仅允许 const 成员访问的基础 class(无需将其设为虚拟)。然后从那里分支到 MutableList 和 ImmutableList,并让 SortedArrayList 从 Immutable 列表派生。