I have a question about Operator Overloading in C++.
For an assignment, I have to write a class which encompasses an array, sort of like the ArrayList in Java.
One of the things I have to do is keep track of the size of the array. Size is the amount of elements included, whereas capacity is the maximum amount which CAN be included before the class has to expand the array.
Client code specifies the size when they call the constructor. However, when new elements are added, I have to figure out a way to change the size.
My teacher said something about being able to overload an operator for different sides of an equality. Is this a real thing, or did I misunderstand her? If this works, it would be the optimal solution to my problem.
My current overloading for the [] operator is:
int & ArrayWrapper::operator [] (int position){
if(position == _size){
if(_size == _capacity){
changeCapacity(_capacity+10);
}
}
return _array[position];
}
This works fine for retrieval, but I'd like to have it so that if someone calls it from the left hand side of a '=' then it checks to see if it needs to expand the size or not.
EDIT: If this isn't a real thing, can anyone think of a different solution to the problem? One solution I thought of is to have the getSize() method just go through the entire array every time it is called, but I'd really rather not use that solution because it seems cheesy.
EDIT: For clarification, I'm not asking whether or not my expansion of an array works. I need to add 1 to size every time a new element is added. For example, if the client creates an array of size 15 and capacity 25, and then tries to add something to Array[15], that SHOULD increase the size to 16. I was wondering if there was a way to do that with overloading.
A simple approach, which doesn't quite do what you want, is to overload on whether the array is const or mutable.
This doesn't distinguish between whether the array is being used on the left-hand side of assignment (as a lvalue) or on the right (as a rvalue); just on whether it's allowed to be modified or not.
// Mutable overload (returns a mutable reference)
int & operator[](size_t position) {
if (position >= _size) {
if (position >= _capatity) {
// increase capacity
}
// increase size
}
return _array[position];
}
// Const overload (returns a value or const reference)
int operator[](size_t position) const {
if (position >= _size) {
throw std::out_of_range("Array position out of range");
}
return _array[position];
}
If you really want to tell whether you're being assigned to or not, then you'll have to return a proxy for the reference. This overloads assignment to write to the array, and provides a conversion operator to get the value of the element:
class proxy {
public:
proxy(ArrayWrapper & array, size_t position) :
_array(array), _position(position) {}
operator int() const {
if (_position >= _array._array._size) {
throw std::out_of_range("Array position out of range");
}
return _array._array[_position];
}
proxy & operator=(int value) {
if (_position >= _size) {
if (_position >= _capatity) {
// increase capacity
}
// increase size
}
_array._array[_position] = value;
return *this;
}
private:
ArrayWrapper & _array;
size_t _position;
};
You probably need to declare this a friend of ArrayWrapper; then just return this from operator[]:
proxy ArrayWrapper::operator[](size_t position) {
return proxy(*this, position);
}
This approach is fine. There's an error in the code, though: what happens if someone calls that operator with a position that's equal to the current size of the array plus 100?
The question is whether you really want different behavior depending on
which side of the = you are. Your basic idea will work fine, but will
expand the array regardless of the side you're on, e.g.:
ArrayWrapper a(10);
std::cout << a[20] << std::end;
will result in expanding the array. Most of the time, in such cases,
the preferred behavior would be for the code above to raise an exception,
but for
ArrayWrapper a(10);
a[20] = 3.14159;
to work. This is possible using proxies: first, you define double
ArrayWrapper::get( int index ) const and void ArrayWrapper::set( int
index, double newValue ); the getter will throw an exception if the
index is out of bounds, but the setter will extend the array. Then,
operator[] returns a proxy, along the lines of:
class ArrayWrapper::Proxy
{
ArrayWrapper* myOwner;
int myIndex;
public:
Proxy( ArrayWrapper& owner, int index )
: myOwner( &owner )
, myIndex( index )
{
}
Proxy const& operator=( double newValue ) const
{
myOwner->set( myIndex, newValue );
}
operator double() const
{
return myOwner->get( myIndex );
}
};
In case you're not familiar with the operator double(), it's an
overloaded conversion operator. The way this works is that if the
operator[] is on the left side of an assignment, it will actually be
the proxy which gets assigned to, and the assignment operator of the
proxy forwards to the set() function. Otherwise, the proxy will
implicitly convert to double, and this conversion forwards to the
get() function.
Related
How can I overload the operators of a class, so that using syntax of
classInstance[index] = value;
performs
classInstance.cfgfile.Write(index,value)
background info; feel free to skip.
The application we develop uses a memory-mapped access to a segment of NVRAM - actually, mapped are just two registers, address and data. You write to the address register, then either write or read the data register. After initialization, the reads and writes are performed by a simple [] overload of the class holding the reference to the segment of memory. You refer to the instance's [] giving a namespaced index of the cell you want to read and write and it does its thing.
int& IndirectMemory::operator[](RTCMemIndex idx)
{
*midx_reg = idx;
return *mdata_reg;
}
(code stripped of irrelevant elements like mutexes and sanity checks).
Everything works fine... as long as the NVRAM works fine. This specific chip is out of production, and the ones 'out in the wild' began dying of old age currently. Their functionality is of low significance to our use, and we could shift their role to the flash memory with nearly no impact (just a little more flash wear) if the chip goes corrupt. Thing is we want to use the flash record using our config format, which uses getters and setters.
int TCfgFile::ReadKey(std::string Key);
void TCfgFile::WriteKey(std::string Key,int data);
And in many places of the code we have calls to NVRAM through IndirectMemory[Some_Register] = Some_Value; writting assorted things that change frequently and we want to persist through reboot. I'd like to retain this syntax and behavior, but be able to write to the file if NVRAM is detected to be corrupted or manually disabled through a config entry.
The net is rife with examples of using operator[] for setting given value just by returning the reference to it. For example:
unsigned long operator [](int i) const {return registers[i];}
unsigned long & operator [](int i) {return registers[i];}
In that case if I call, say, reg[3] = 1; the [] will return a reference to the element#3 and the default operator= will write to the reference just fine.
But since I can't return a reference to a key in the file (.WriteKey() just performs a complete write, returning success or error), and operator= doesn't take an index, I'm afraid this simple option won't help.
You can use a proxy class to solve this. Since value can't be passed into classInstance we need to make an object that operator[] can return that will get the value of value and knows which instance to apply the operation to. Using
struct Proxy
{
classInstance_type& to_apply;
index_type index;
Proxy(classInstance_type& to_apply, index_type index) : to_apply(to_apply), index(index) {}
Proxy& operator=(value_type const & value)
{
to_apply.cfgfile.Write(index,value)
return *this;
}
};
your class's operator[] would look like
Proxy operator[](index_type index)
{
return Proxy{*this, index};
}
and then when you do classInstance[index] = value; you call Proxy's operator= which has a reference to the object to call, the index to use, and the value you also need.
You can also do this without a proxy class. You can make operator[] return a reference to *this and than overload the = operator of said class to perform Write on whatever was given to operator= in the second argument.
#include <iostream>
struct Foo {
void Write(int idx, int value) {
std::cout << "Write(" << idx << ", " << value << ")\n";
}
Foo& operator[](int idx) {
this->index = idx;
return *this;
}
void operator=(int value) {
this->Write(this->index, value);
}
int index;
};
int main() {
Foo f;
f[5] = 10;
}
Prints: Write(5, 10)
I implemented SpareArray class. It's a big one, so there is no sense to show it all, but what is important is that is has insert method, which makes it possible to insert values at any index (from zero to "infinity"). So, my client code may look like so:
auto arr = new SpareArray<int>{};
arr.insert(100, 1);
The above code inserts value 1 at index 100. Now, I want to be able to use square brakets notation to get the same result:
auto arr = new SpareArray<int>{};
arr[100] = 1; //I want this line to internally call arr.insert(100, 1);
So, how can operator[] be difined to internally call insert method? I need this call, because insert method has some business logic and I want this business logic to take place also in this case (in case of []).
You can return a proxy that does the final call to insert and acts as a wrapper around an element of your array.
template<typename T>
struct Proxy {
Proxy(SpareArray<T> &This, std::size_t Index) : This(This), Index(Index) {}
T &operator=(const T &Value) {
This.insert(Index, Value);
return This.get(Index); // or however you get an element
}
operator T&() { return This.get(Index); }
operator const T&() const { return This.get(Index); }
private:
SpareArray<T> &This;
std::size_t Index;
};
For simplicity, I didn't add more but I would expect such a class to have proper noexcept semantics and a move assignment operator, for example. You also might want to make sure that you delete the copy constructors and such.
I overloaded array subscript ( [] ) operator. I have made it return an integer as I wont be using it for any assignment purposes. However, I am unable to use the comparison operator now!
Here is the code
class Set
{
public:
virtual int operator[](int i) = 0;
virtual int size() = 0;
void union_operation(Set* second);
void interesction_operation(Set* second);
};
void Set::union_operation(Set* second)
{
int second_size = second->size();
for(int i=0;i<second_size;i++)
{
for(int j=0;j<this->size();j++)
{
//The line below doesnt work!
if(this[j]==second[i])
{
break;
}
}
}
}
The implementation of operator overloading is carried out in a derived class.
Since the overloaded operator will return an integer, hence the comparison is between two integers, which is perfectly valid. Why does this line still give an error?
In C++, this is a pointer that requires dereferencing before you can use it. Unless you're passing it to a function of course.
So, in order for your comparison to work, it should look like the following:
if((*this)[j] == (*second)[i])
{
break;
}
EDIT: second is also a Set pointer so you must dereference it to use it too.
I have a simple class that I am storing in a vector as pointers. I want to use a find on the vector but it is failing to find my object. Upon debugging it doesn't seem to call the == operator I've provided. I can 'see' the object in the debugger so I know its there. The code below even uses a copy of the first item in the list, but still fails. The only way I can make it pass is to use MergeLine* mlt = LineList.begin(), which shows me that it is comparing the objects and not using my equality operator at all.
class MergeLine {
public:
std::string linename;
int StartIndex;
double StartValue;
double FidStart;
int Length;
bool operator < (const MergeLine &ml) const {return FidStart < ml.FidStart;}
bool operator == (const MergeLine &ml) const {
return linename.compare( ml.linename) == 0;}
};
Class OtherClass{
public:
std::vector<MergeLine*>LineList;
std::vector<MergeLine*>::iterator LL_iter;
void DoSomething( std::string linename){
// this is the original version that returned LineList.end()
// MergeLine * mlt
// mlt->linename = linename;
// this version doesn't work either (I thought it would for sure!)
MergeLine *mlt =new MergeLine(*LineList.front());
LL_iter = std::find(LineList.begin(), LineList.end(), mlt);
if (LL_iter == LineList.end()) {
throw(Exception("line not found in LineList : " + mlt->linename));
}
MergeLine * ml = *LL_iter;
}
};
cheers,
Marc
Since your container contains pointers and not objects, the comparison will be between the pointers. The only way the pointers will be equal is when they point to the exact same object. As you've noticed the comparison operator for the objects themselves will never be called.
You can use std::find_if and pass it a comparison object to use.
class MergeLineCompare
{
MergeLine * m_p;
public:
MergeLineCompare(MergeLine * p) : m_p(p)
{
}
bool operator()(MergeLine * p)
{
return *p == *m_p;
}
};
LL_iter = std::find_if(LineList.begin(), LineList.end(), MergeLineCompare(mlt));
I think what you really want is to use std::find_if like this:
struct MergeLineNameCompare
{
std::string seachname;
MergeLineNameComp(const std::string &name) : seachname(name)
{
}
bool operator()(const MergeLine * line)
{
return seachname.compare( line->linename ) == 0;
}
};
LL_iter = std::find_if(LineList.begin(), LineList.end(), MergeLineNameCompare(linename) );
The operator == (no matter wich form) is better saved for real comparison of equality.
Operator overloading can't work with pointers as it is ambiguous.
Bjarne Stroustrup :-
References were introduced primarily to support operator overloading.
C passes every function argument by value, and where passing an object
by value would be inefficient or inappropriate the user can pass a
pointer. This strategy doesn’t work where operator overloading is
used. In that case, notational convenience is essential so that a user
cannot be expected to insert address− of operators if the objects are
large.
So, may be not best but still :-
std::vector<MergeLine>LineList;
std::vector<MergeLine>::iterator LL_iter;
I want to represent my object like an array. I mean that the programmer can write in his code
myobject[3]=2
In the back (in myobject code) there isn't an array at all, it's only representation.
So I need to overload [] and = simultaneously.
How can this be done?
thank you,
and sorry about my poor English.
operator[] should return a reference to object you are trying to modify. It may be some kind of metaobject, that overloads operator= to do whatever you wish with your main object.
Edit: As the OP clarified the problem, there is a way to do this. Look here:
#include <vector>
#include <iostream>
int & func(std::vector<int> & a)
{
return a[3];
}
int main()
{
std::vector<int> a;
a.push_back(1);
a.push_back(2);
a.push_back(3);
a.push_back(4);
func(a) = 111;
std::cout << a[3] << std::endl;
}
So I need to overload [] and = simultaneity. How can it's can be done?
It can't be done. What you can do instead is override operator[] to return a 'proxy reference'. That is, an object that has knowledge of the object 'myobject' to which it was applied and the index used '3', and provides appropiate conversion operators to the mapped type (I pressume int) as well as assignment operators. There are a few examples of proxy references in the standard library itself. Something in the lines of:
class proxy
{
public:
proxy( object& object, int index ) : _object( object ), _index( index ) {}
operator int() const { return _object.implementation.value_at( index ); }
proxy operator=( int value ){ _object.implementation.value_at( index, value ); return *this; }
private:
object& _object;
int _index;
};
#yoni: It is possible to give address of any member of the vector (as long as it exists). Here's how it's done.
int& MyObject::operator[](size_t index)
{
return mVector[index];
}
const int& MyObject::operator[](size_t index) const
{
return mVector[index];
}
This is possible because std::vector is guaranteed to be storing elements in a contiguous array. The operator[] of std::vector returns a reference-type of the value it stores. By you overloading the operator[], you just need to pass that reference out of your operator[] function.
NOTE: std::vector will take care of bounds check. With the solution that #Griwes gives, there's no bounds checking.
EDIT: Seems like Griwes has edited his solution.