Related
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...
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?
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.
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.
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[]
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.
(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.)
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:
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.
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.
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
Return a proxy object which will have:
operator=(Type const &) overloaded for writes
operator Type() for reads
in the access example you give you can get a distinction by using a const version:
const Type& operator [] ( int index ) const;
on a sidenote, using size_t as index gets rid of the need for checking if index >= 0
#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;
}
I'm trying to build a function in which an argument is a reference to a vector of objects. In this case name of the object is 'obj', it is an instance of the class 'Example' and it is a vector as defined in vector class. Object have members like x, y and z.
The reason I'm trying with passing references is because I want to change the value of obj.z by making use of obj.x and obj.y from inside of the function.
#include <vector>
#include <iostream>
using namespace std;
class Example{
public:
double x;
double y;
double z;
Example()
: x(0.0),y(0.0){
}
};
void calculate(Example &obj, int M) {
for(int i = 0; i < M; i++) {
obj[i].z = obj[i].x * obj[i].y;
}
}
int main() {
vector<Example> obj;
int N = 10;
calculate(obj, N);
}
When I run this, I get the following errors:
Inside of the function I have: "Type 'Example' does not provide a subscript operator."
I google'd it and saw that it is related to operator overloading and usage of references. The solution is probably related to dereferencing my object reference inside of the function, but I wasn't able to manage this one right currently.
And the second error is outside of the function, inside the main() at the line where I call the function: "No matching function for call to 'calculate'".
Here, I assume the error is related to the fact that obj is not just an object but a vector of objects, so I should change the argument somehow. But I haven't been able to correct this one up to now as well.
So, to summarize, I want to pass a vector of objects to a function as reference, because I want to be able to change a member of the object inside of the function.
Thank you in advance.
I want to pass a vector of objects to a function as reference
So do that:
void calculate(vector<Example> &obj) {
for(int i = 0; i < obj.size(); i++) {
obj[i].z = obj[i].x * obj[i].y;
}
}
int main() {
vector<Example> obj;
// put some values into it...
calculate(obj);
}
I believe you wanted your function to be
void calculate(vector<Example> &obj, int M)
and not
void calculate(Example &obj, int M)
obj[i]
'obj' isn't an array.
You need to declare it as:
void calculate(Example* obj, int M)
And rather
void calculate(vector<Example>& v)
I'd like to use sort() to do the following
I have a text char[] T which is (private) member of a class. The text has length n.
I also ave an array int[] P that contains the first n integers.
I'd like to std::sort P such that the lexicographic order among suffixes of T are preserved
i.e., for any i < j we have that T[P[i]...n] is lex smaller than T[P[j]...n].
I'm able to do it when char[] T is a global variable by defining
bool myfunction (int i,int j) {
int m = i, l = j;
while(m<n and l <n) {
if(T[m] != T[l]) return (T[m]<T[l]);
m++; l++;
}
return (m<l);
}
and calling std::sort(P, P+n, myfuction)
I'm in truble when T is a member of an object (and sort is called by a method of that object).
How can I define myfunction so that T is visible?
Should it be member of that object? If yes, how?
Thank you very much.
Edit: bool instead of int
As you guessed, one way to do it is by defining yourfunction() as a public member of that class.
Example:
#include <algorithm>
#include <vector>
using namespace std;
class T
{
private:
int value;
public:
T()
{
value = rand() % 100;
}
static bool Compare(const T &a, const T &b)
{
return a.value < b.value;
}
};
int main(int argc, char** argv)
{
vector<T> data;
//add some data
for (int i=0; i<10; i++)
data.push_back(T());
//sort using static method that can access private attributes
std::sort(data.begin(), data.end(), T::Compare);
}
If sort represents std::sort, the function that you are using as predicate is wrong for a couple of reasons, the first of which is that the returned type from the function should be a bool and not an int.
The next thing that is wrong is that the predicate is required to be consistent, that is, given two inputs a and b the result of predicate( a, b ) must be either true or false and always the same. If that condition is not met, the result of calling sort will be undefined, possibly including an infinite loop.
The approach (rather than the predicate itself) is probably not good either, as the number of times that the predicate will be called depends on the input data, and the results of the different calls to the predicate (until the algorithm thinks, that according to your partial order, the sequence is sorted).
You probably need a functor object:
struct myfunctor {
const char *T;
size_t n;
myfunctor(const char *T, size_t n) : T(T), n(n) {}
bool operator()(int i, int j) {
// stuff using T and n
}
// Optionally, something along these lines, I haven't tested it
template <size_t N> myfunctor(const char (&x)[N]) : T(&x[0]), n(N) {}
template <size_t N> myfunctor(char (&x)[N]) : T(&x[0]), n(N) {}
};
SomeObjectContainingT x;
std::sort(P, P+n, myfunctor(x.T, x.n));
Or if x.T is an actual array rather than just a pointer, the template constructors will capture the array size from the type, no need for a second parameter:
std::sort(P, P+n, myfunctor(x.T));
Edit: sorry, missed that T is private. I think you have two issues here, scope and accessibility. The functor solves the scope problem, now for the accessibility.
If you want external functions to access T, x must provide a means to access it. For example, it could return the functor object:
class SomeObjectContaining T {
char T[23];
public:
myfunctor comparator() { return myfunctor(T); }
};
std::sort(P, P+n, x.comparator());
Or you could mess about with friend: define your functor class as a friend of SomeObjectContainingT, then pass the object to its constructor rather than the array.
I am currently in a collage second level programing course... We are working on operator overloading... to do this we are to rebuild the vector class...
I was building the class and found that most of it is based on the [] operator. When I was trying to implement the + operator I run into a weird error that my professor has not seen before (apparently since the class switched IDE's from MinGW to VS express...) (I am using Visual Studio Express 2008 C++ edition...)
Vector.h
#include <string>
#include <iostream>
using namespace std;
#ifndef _VECTOR_H
#define _VECTOR_H
const int DEFAULT_VECTOR_SIZE = 5;
class Vector
{
private:
int * data;
int size;
int comp;
public:
inline Vector (int Comp = 5,int Size = 0)
: comp(Comp), size(Size) { if (comp > 0) { data = new int [comp]; }
else { data = new int [DEFAULT_VECTOR_SIZE];
comp = DEFAULT_VECTOR_SIZE; }
}
int size_ () const { return size; }
int comp_ () const { return comp; }
bool push_back (int);
bool push_front (int);
void expand ();
void expand (int);
void clear ();
const string at (int);
int& operator[ ](int);
int& operator[ ](int) const;
Vector& operator+ (Vector&);
Vector& operator- (const Vector&);
bool operator== (const Vector&);
bool operator!= (const Vector&);
~Vector() { delete [] data; }
};
ostream& operator<< (ostream&, const Vector&);
#endif
Vector.cpp
#include <iostream>
#include <string>
#include "Vector.h"
using namespace std;
const string Vector::at(int i) {
this[i];
}
void Vector::expand() {
expand(size);
}
void Vector::expand(int n ) {
int * newdata = new int [comp * 2];
if (*data != NULL) {
for (int i = 0; i <= (comp); i++) {
newdata[i] = data[i];
}
newdata -= comp;
comp += n;
data = newdata;
delete newdata;
}
else if ( *data == NULL || comp == 0) {
data = new int [DEFAULT_VECTOR_SIZE];
comp = DEFAULT_VECTOR_SIZE;
size = 0;
}
}
bool Vector::push_back(int n) {
if (comp = 0) { expand(); }
for (int k = 0; k != 2; k++) {
if ( size != comp ){
data[size] = n;
size++;
return true;
}
else {
expand();
}
}
return false;
}
void Vector::clear() {
delete [] data;
comp = 0;
size = 0;
}
int& Vector::operator[] (int place) { return (data[place]); }
int& Vector::operator[] (int place) const { return (data[place]); }
Vector& Vector::operator+ (Vector& n) {
int temp_int = 0;
if (size > n.size_() || size == n.size_()) { temp_int = size; }
else if (size < n.size_()) { temp_int = n.size_(); }
Vector newone(temp_int);
int temp_2_int = 0;
for ( int j = 0; j <= temp_int &&
j <= n.size_() &&
j <= size;
j++) {
temp_2_int = n[j] + data[j];
newone[j] = temp_2_int;
}
////////////////////////////////////////////////////////////
return newone;
////////////////////////////////////////////////////////////
}
ostream& operator<< (ostream& out, const Vector& n) {
for (int i = 0; i <= n.size_(); i++) {
////////////////////////////////////////////////////////////
out << n[i] << " ";
////////////////////////////////////////////////////////////
}
return out;
}
Errors:
out << n[i] << " "; error C2678:
binary '[' : no operator found which
takes a left-hand operand of type
'const Vector' (or there is no
acceptable conversion)
return newone;
error C2106: '=' : left
operand must be l-value
As stated above, I am a student going into Computer Science as my selected major I would appreciate tips, pointers, and better ways to do stuff :D
This:
int operator[ ](int);
is a non-const member function. It means that it cannot be called on a const Vector.
Usually, the subscript operator is implemented such that it returns a reference (if you return a value, like you are doing, you can't use it as an lvalue, e.g. you can't do newone[j] = temp_2_int; like you have in your code):
int& operator[](int);
In order to be able to call it on a const object, you should also provide a const version of the member function:
const int& operator[](int) const;
Since you ask for "tips, pointers, and better ways to do stuff:"
You cannot name your include guard _VECTOR_H. Names beginning with an underscore followed by a capital letter are reserved for the implementation. There are a lot of rules about underscores.
You should never use using namespace std in a header.
Your operator+ should take a const Vector& since it is not going to modify its argument.
Your at should return an int and should match the semantics of the C++ standard library containers (i.e., it should throw an exception if i is out of bounds. You need to use (*this)[i] to call your overloaded operator[].
You need to learn what the * operator does. In several places you've confused pointers and the objects to which they point.
Watch out for confusing = with == (e.g. in if (comp = 0)). The compiler will warn you about this. Don't ignore warnings.
Your logic will be much simpler if you guarantee that data is never NULL.
Can't fit this into a comment on Neil's answer, so I'm gonna have to go into more detail here.
Regarding your expand() function. It looks like this function's job is to expand the internal storage, which has comp elements, by n elements, while maintaining the size of the Vector. So let's walk through what you have.
void Vector::expand(int n) {
int * newdata = new int [comp * 2];
Okay, you just created a new array that is twice as big as the old one. Error: Why doesn't the new size have anything to do with n?
if (*data != NULL) {
Error: *data is the first int element in your array. It's not a pointer. Why is it being compared to NULL?
Concept Error: Even if you said if (data != NULL), which could be a test to see if there is an array at all, at what point in time is data ever set to NULL? new [] doesn't return NULL if it's out of memory; it throws an exception.
for (int i = 0; i <= (comp); i++) {
newdata[i] = data[i];
}
Warning: You're copying the whole array, but only the first size elements are valid. The loop could just run up to size and you'd be fine.
newdata -= comp;
Error: Bad pointer math. newdata is set to a pointer to who knows where (comp ints back from the start of newdata?!), and almost certainly a pointer that will corrupt memory if given to delete [].
comp += n;
This is fine, for what it is.
data = newdata;
delete newdata;
}
Error: You stored a pointer and then immediately deleted its memory, making it an invalid pointer.
else if ( *data == NULL || comp == 0) {
data = new int [DEFAULT_VECTOR_SIZE];
comp = DEFAULT_VECTOR_SIZE;
size = 0;
}
}
Error: This should be in your constructor, not here. Again, nothing ever sets data to NULL, and *data is an int, not a pointer.
What this function should do:
create a new array of comp + n elements
copy size elements from the old array to the new one
delete the old array
set data to point to the new array
Good luck.
Besides of what others already wrote about your operator[]():
Your operator+() takes the right-hand side per non-const reference - as if it would attempt to change it. However, with A+B everyone would expect B to remain unchanged.
Further, I would implement all binary operators treating their operands equally (i.e., not changing either of them) as non-member functions. As member functions the left-hand side (this) could be treated differently. (For example, it could be subjected to overwritten versions in derived classes.)
Then, IME it's always good to base operator+() on operator+=(). operator+=() does not treat its operands equally (it changes its left one), so it's best done as a member function. Once this is done, implementing operator+() on top of it is a piece of cake.
Finally, operator+() should never, never ever return a reference to an object. When you say A+B you expect this to return a new object, not to change some existing object and return a reference to that.
There are so many errors in your code that it is hard to know where to start. Here's one:
delete [] data;
*data = *newdata;
You delete a pointer and then immediately dereference it.
And:
const string Vector::at(int i) {
this[i];
}
This is (I think) a vector of ints. why is this returning a string? And applying the [] operator to this does not call your operator[] overload - it treats this as an array, which it isn't.
You need to provide two versions of your operator[]. For accessing:
T operator[](std::size_t idx)const;
For writing to the element:
T& operator[](std::size_t idx);
In both of the above, replace T with the type of the elements. The reason you have this problem is that only functions that are marked "const" may be invoked on an object declared to be "const". Marking all non-mutating functions as "const" is definitely something you should do and is called "const-correctness". Since returning a reference to an element (necessary for writing) allows the underlying object to be mutated, that version of the function cannot be made "const". Therefore, a read-only "const" overload is needed.
You may also be interested in reading:
Const Correctness from the C++ FAQ Lite
Const Correctness in C++
int Vector::operator[] (int place) { return (data[place]); }
This should be
int Vector::operator[] (int place) const { return (data[place]); }
so that you will be able to do the [] operation on const vectors. The const after the function declaration means that the class instance (this) is treated as const Vector, meaning you won't be able to modify normal attributes. Or in other words: A method that only has read access to attributes.
I'm using a 2D matrix in one of my projects. It's something like it is suggested at C++ FAQ Lite.
The neat thing is that you can use it like this:
int main()
{
Matrix m(10,10);
m(5,8) = 106.15;
std::cout << m(5,8);
...
}
Now, I have a graph composed of vertices and each vertex has a public (just for simplicity of the example) pointer to 2D matrix like above. Now I do have a pretty ugly syntax to access it.
(*sampleVertex.some2DTable)(0,0) = 0; //bad
sampleVertex.some2DTable->operator()(0,0) = 0; //even worse...
Probably I'm missing some syntactic sugar here due to my inexperience with operator overloading. Is there a better solution?
Consider using references instead of pointers (provided, it can't be null and you can initialize in the constructor).
Consider making a getter or an instance of a matrix wrapper class for a vertex that returns a reference to 2D matrix (provided, it can't be null).
sampleVertex.some2DTable()(0,0) = 0;
sampleVertex.some2DTableWrap(0,0) = 0;
However, to me it sounds like a non-issue to justify going through all the trouble.
If you have a pointer to a Matrix, e.g. as a function parameter that you can't make a reference (legacy code, e.g.), you can still make a reference to it (pseudo code):
struct Matrix {
void operator () (int u, int v) {
}
};
int main () {
Matrix *m;
Matrix &r = *m;
r (1,1);
}
You're basically limited to (*sampleVertex.some2DTable)(0,0). Of course, if you don't need reseating, why not store the actual values in the matrix instead?
Alternatively, make the pointer private and make an accessor (note: the following examples assume a matrix of EntryTypes):
Matrix& Vertex::GetTableRef()
{
return *some2DTable;
}
// or
Matrix::EntryType& Vertex::GetTableEntry(int row, int col)
{
return (*some2DTable)(row,col);
}
// way later...
myVertex.GetTableRef()(0,0) = 0;
// or...
myVertex.GetTableEntry(0,0) = 0;
Or, just define an inline function to do this for you if you can't change the class Vertex:
// in some header file
inline Matrix& GetTableRef(Vertex& v)
{
return *v.some2DTable;
}
// or you could do this
inline Matrix::EntryType& GetTableEntry(Vertex& v, int row, int col)
{
return (*v.some2DTable)(row, col);
}
// later...
GetTableRef(myVertex)(0, 0) = 0;
// or
GetTableEntry(myVertex, 0, 0) = 0;
Finally, don't forget that you don't have to use operator overloading. STL collections implement an at() member function, which is checked, as opposed to operator[] which is unchecked. If you don't mind the overhead of bounds checking, or if you just want to be nonstandard, you could implement at() and then just call myVertex.some2DTable->at(0,0), saving a bit of a syntactic headache altogether.
There is no C++ syntactic sugar that will ease the pain of what you describe:
(*sampleVertex.some2DTable)(0,0) = 0; //bad
sampleVertex.some2DTable->operator()(0,0) = 0; //even worse...
In this situation, I would either have the graph return a reference instead of a pointer, or have the matrix define a function which calls the operator():
inline matrixType &Matrix::get( int x, int y ){ return operator()(x,y); }
Then, the syntax isn't quite as ugly for the vertex example:
sampleVertex.some2DTable->get(0,0) = 0;
I would add a function that returns you a ref like rlbond recommends. For a quick fix or if you don't have control over the source of it, i would go with this:
sampleVertex.some2DTable[0](0,0) = 0; // more readable
That's actually equivalent, because the following holds if a is a pointer to a defined class:
*a == *(a + 0) == a[0]
See this long discussion on comp.lang.c++ about that same problem with good answers.
This is the best way without changing your code:
//some2DTable is a pointer to a matrix
(*sampleVertex.some2DTable)(0,0)
You could also instead make some2DTable a reference to a matrix instead of a pointer to a matrix. Then you would have simplified syntax as in your first code sniplet.
//some2DTable is a reference to a matrix instead of a pointer to a matrix
sampleVertex.some2DTable(0,0)
Or you could keep some2DTable a pointer to a reference and simply store a reference variable to it and use that in the context of your code block.
I'd change the way you get hold of "sampleVertex.some2DTable" so it returns a reference.
Either that or create the reference yourself:
Matrix& m = *sampleVertex.some2DTable;
m(1,2) = 3;
I don't know if it's worth the trouble, but you could do:
class MatrixAccessor {
private:
Matrix2D* m_Matrix;
public:
MatrixAccessor(Matrix2D* matrix) : m_matrix(matrix) { }
double& operator()(int i, int j) const { return (*m_Matrix)(i,j); }
Matrix2D* operator->() const { return m_Matrix; }
void operator=(Matrix2D* matrix) { m_Matrix = matrix; }
};
Provided the original operator() returns a reference (as it is in many matrix classes).
Then you provide that MatrixAccessor in your vertex class:
class Vertex {
Matrix2D* myMatrix;
public:
MatrixAccessor matrix;
Vertex(Matrix2D *theMatrix) : myMatrix(theMatrix), matrix(theMatrix) { }
};
Then you can write:
Vertex v;
v.matrix(1,0) = 13;
v.matrix->SomeOtherMatrixOperation();
EDIT
I added const keywords (thanks to #phresnel for bringing up the topic) in order to make the solution semantically equivalent to a solution only presenting a public Matrix2D-pointer.
An advantage of this solution is that constness could be transferred to the matrix object by adding two non-const versions of the operator()() and operator->() (i.e. the matrix cannot be modified on const vertices) and changing the const ones to return a const double& and const Matrix2D* respectively.
That would not be possible when using a public pointer to the matrix object.
You could implement Matrix::operator (int,int) by calling a member function and use that one directly when dealing with pointers.
class Matrix
{
public:
float ElementAt( int i, int j ) const { /*implement me*/ }
float operator() ( int i, int j ) const { return ElementAt( i, j ); }
...
};
void Foo(const Matix* const p)
{
float value = p->ElementAt( i, j );
...
}
void Bar(const Matrix& m)
{
float value = m(i,j);
}