以允许响应更新的方式重载 C++ 索引下标运算符 []
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3581981/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Overloading the C++ indexing subscript operator [] in a manner that allows for responses to updates
提问by DuncanACoulter
Consider the task of writing an indexable class which automatically synchronizes its state with some external data-store (e.g. a file). In order to do this the class would need to be made aware of changes to the indexed value which might occur. Unfortunately the usual approach to overloading operator[] does not allow for this, for example...
考虑编写一个可索引类的任务,该类自动将其状态与一些外部数据存储(例如文件)同步。为了做到这一点,类需要知道可能发生的对索引值的更改。不幸的是,重载 operator[] 的常用方法不允许这样做,例如......
Type& operator[](int index)
{
assert(index >=0 && index < size);
return state[index];
}
I there any way to distinguish between a value being accessed and a value being modified?
我有什么方法可以区分正在访问的值和正在修改的值吗?
Type a = myIndexable[2]; //Access
myIndexable[3] = a; //Modification
Both of these cases occur after the function has returned. Is there some other approach to overloading operator[] which would perhaps make more sense?
这两种情况都发生在函数返回之后。是否有其他一些方法来重载 operator[] 可能更有意义?
采纳答案by Martin York
From the operator[] you can only really tell access.
Even if the external entity uses the non cost version this does not mean that a write will take place rather that it could take place.
从 operator[] 中,您只能真正了解访问权限。
即使外部实体使用非成本版本,这也不意味着写入将发生,而是可能发生。
As such What you need to do is return an object that can detect modification.
The best way to do this is to wrap the object with a class that overrides the operator=
. This wrapper can then inform the store when the object has been updated. You would also want to override the operator Type
(cast) so that a const version of the object can be retrieved for read accesses.
因此,您需要做的是返回一个可以检测修改的对象。
执行此操作的最佳方法是使用覆盖operator=
. 当对象被更新时,这个包装器然后可以通知商店。您还需要覆盖operator Type
(cast) 以便可以检索对象的 const 版本以进行读取访问。
Then we could do something like this:
然后我们可以做这样的事情:
class WriteCheck;
class Store
{
public:
Type const& operator[](int index) const
{
return state[index];
}
WriteCheck operator[](int index);
void stateUpdate(int index)
{
// Called when a particular index has been updated.
}
// Stuff
};
class WriteCheck
{
Store& store;
Type& object;
int index;
public: WriteCheck(Store& s, Type& o, int i): store(s), object(o), index(i) {}
// When assignment is done assign
// Then inform the store.
WriteCheck& operator=(Type const& rhs)
{
object = rhs;
store.stateUpdate(index);
}
// Still allow the base object to be read
// From within this wrapper.
operator Type const&()
{
return object;
}
};
WriteCheck Store::operator[](int index)
{
return WriteCheck(*this, state[index], index);
}
An simpler alternative is:
Rather than provide the operator[] you provide a specific set method on the store object and only provide read access through the operator[]
一个更简单的替代方法是:
不是提供 operator[],而是在 store 对象上提供特定的 set 方法,并且仅通过 operator[] 提供读取访问
回答by Tony Delroy
You can have (the non-const) operator[] return a proxy object that keeps a reference or pointer to the container, and in which operator= signals the container of the update.
您可以让(非常量)operator[] 返回一个代理对象,该对象保留对容器的引用或指针,其中 operator= 表示更新的容器。
(The idea of using const vs non-const operator[] is a red herring... you may know that you've just given away non-const access to the object, but you don't know if that access is still being used for a read or a write, when that write completes, or have any mechanism for updating the container thereafter.)
(使用 const 与非常量运算符 [] 的想法是一个红鲱鱼......您可能知道您刚刚放弃了对对象的非常量访问,但您不知道该访问是否仍然存在用于读取或写入,当写入完成时,或具有任何用于此后更新容器的机制。)
回答by dzilbers
Another elegant (IMHO) solution... Actually it is based on the fact that the const overload is called only when used on const object. Lets first create two [] overloads - as it is required, but using different locations:
另一个优雅的(恕我直言)解决方案......实际上它是基于这样一个事实,即仅当在 const 对象上使用时才调用 const 重载。让我们首先创建两个 [] 重载 - 根据需要,但使用不同的位置:
Type& operator[](int index)
{
assert(index >=0 && index < size);
return stateWrite[index];
}
const Type& operator[](int index) const
{
assert(index >=0 && index < size);
return stateRead[index];
}
Now you should create a shadow reference of your object when you need to "read" it as follows:
现在,当您需要“读取”对象时,您应该创建对象的阴影引用,如下所示:
const Indexable& myIndexableRead = myIndexable; // create the shadow
Type a = myIndexableRead[2]; //Access
myIndexable[3] = a; //Modification
Creating this shadow declaration does not actually create anything in the memory. It just creates another name for your object with "const" access. It is all resolved at the compilation stage (including usage of const overload) and does not affect anything in runtime - neither memory nor performance.
创建此影子声明实际上不会在内存中创建任何内容。它只是为具有“const”访问权限的对象创建另一个名称。这一切都在编译阶段解决(包括使用 const 重载)并且不会影响运行时的任何内容——无论是内存还是性能。
And the bottom line - it is much more elegant (IMHO) than creating any assignment proxies, etc. I must state that the statement "From the operator[] you can only really tell access" is incorrect. According to the C++ Standard, returning dynamically allocatted object or global variable by reference is ultimate way to allow its direct modification, including [] overload case.
最重要的是 - 它比创建任何赋值代理等更优雅(恕我直言)。我必须声明“从操作员[]你只能真正告诉访问”这句话是不正确的。根据 C++ 标准,通过引用返回动态分配的对象或全局变量是允许其直接修改的最终方式,包括 [] 重载情况。
Following code has been tested:
以下代码已经过测试:
#include <iostream>
using namespace std;
class SafeIntArray {
int* numbers;
int size;
static const int externalValue = 50;
public:
SafeIntArray( unsigned int size = 20 ) {
this->size = size;
numbers = new int[size];
}
~SafeIntArray() {
delete[] numbers;
}
const int& operator[]( const unsigned int i ) const {
if ( i < size )
return numbers[i];
else
return externalValue;
}
int& operator[]( const unsigned int i ) {
if ( i < size )
return numbers[i];
else
return *numbers;
}
unsigned int getSize() { return size; }
};
int main() {
SafeIntArray arr;
const SafeIntArray& arr_0 = arr;
int size = arr.getSize();
for ( int i = 0; i <= size ; i++ )
arr[i] = i;
for ( int i = 0; i <= size ; i++ ) {
cout << arr_0[i] << ' ';
}
cout << endl;
return 0;
}
And the results are:
结果是:
20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 50
20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 50
回答by Tomek
Return a proxy object which will have:
返回一个代理对象,它将具有:
- operator=(Type const &) overloaded for writes
- operator Type() for reads
- operator=(Type const &) 为写重载
- 用于读取的运算符 Type()
回答by stijn
in the access example you give you can get a distinction by using a const version:
在您提供的访问示例中,您可以通过使用 const 版本来区分:
const Type& operator [] ( int index ) const;
on a sidenote, using size_t as index gets rid of the need for checking if index >= 0
在旁注中,使用 size_t 作为索引无需检查索引是否 >= 0
回答by Praveer Kumar
#include "stdafx.h"
#include <iostream>
template<typename T>
class MyVector
{
T* _Elem; // a pointer to the elements
int _Size; // the size
public:
// constructor
MyVector(int _size):_Size(_size), _Elem(new T[_size])
{
// Initialize the elemets
for( int i=0; i< _size; ++i )
_Elem[i] = 0.0;
}
// destructor to cleanup the mess
~MyVector(){ delete []_Elem; }
public:
// the size of MyVector
int Size() const
{
return _Size;
}
// overload subscript operator
T& operator[]( int i )
{
return _Elem[i];
}
};
int _tmain(int argc, _TCHAR* argv[])
{
MyVector<int> vec(10);
vec[0] =10;
vec[1] =20;
vec[2] =30;
vec[3] =40;
vec[4] =50;
std::cout<<"Print vector Element "<<std::endl;
for (int i = 0; i < vec.Size(); i++)
{
std::cout<<"Vec["<<i<<"] = "<<vec[i]<<std::endl;
}
return 0;
}