Passing class member function return value by reference in C++
I've been searching for a solution to this problem but can't seem to find one. I'm sure this general question has been asked before somewhere but hopefully you can help with my specific situation...
I have a class template someClass
that contain the following (private) members:
int size_x;
int size_y;
int size_total;
T * grid;
someClass
contains a constructor that looks like this:
someClass (const int x, const int y)
: size_x (x), size_y (y), size_total (x*y)
{
grid = new T [size_total];
}
a copy constructor that looks like this:
someClass (const someClass & rhs)
{
size_x = rhs.size_x;
size_y = rhs.size_y;
size_total = rhs.size_total;
grid = new T [size_total];
memcpy(grid, rhs.grid, size_total*sizeof(T));
}
a member function that looks like this:
T * retGrid (void) const
{
return grid;
}
and an assignment operator that looks like this:
someClass & operator= (const some开发者_运维技巧Class & rhs)
{
if (this != &rhs)
{
size_x = rhs.size_x;
size_y = rhs.size_y;
size_total = rhs.size_total;
grid = new T [size_total];
memcpy(grid, rhs.grid, size_total*sizeof(T));
}
return *this;
}
I'm trying to pass the following two someClass
objects
someClass<double> *I1 = new someClass<double>(10,10);
someClass<double> I2 = *I1;
to a function outside of the someClass
class with the following prototype:
int someFunction(double *arr);
This call works fine:
int status;
status = someFunction(I1->retGrid()); // Properly working function call
but this does not
status = someFunction(&I2.retGrid()); // Compiler gives error that says "error: invalid lvalue in unary &"
And if I call someFunction
like this:
status = someFunction(I2.retGrid()); // Compiler gives no error but function returns error value in status
the code compiles but I get a run-time error (a bad status value from another function call within someFunction
).
How can I properly pass I2
to someFunction
?
Many thanks...
You are trying to take address of the temporary object (in this case, a pointer but this doesn't really matter) returned by retGrid
. Hence you cannot use the &
approach.
Without the ampersand, you would pass the internal array from I2
to someFunction
. If that's not fine for you (i.e. since you get some kind of runtime error), consider making a copy of this array and passing it to someFunction
instead.
When you have pointers in your class and you have to allocate memory for each object, you need to define a copy constructor and an assignment operator. You only added the later. This initialization
someClass<double> I2 = *I1;
is actually performed with a copy constructor, not with the assignment operator. It is identical to
someClass<double> I2(*I1);
However, it is wrong, because you only allocate memory for grid. But if grid was already allocated (from a previous assignment) you'd leak memory. So it should look like this:
someClass & operator= (const someClass & rhs)
{
if (this != &rhs)
{
size_x = rhs.size_x;
size_y = rhs.size_y;
size_total = rhs.size_total;
delete [] grid;
grid = new T [size_total];
memcpy(grid, rhs.grid, size_total*sizeof(T));
}
return *this;
}
First question: why aren't use using std::vector
, instead of
trying to manage the memory yourself. You don't show the
destructor; I presume you free the memory there. But there are
still problems:
In the copy constructor, you use
memcpy
. That's not a problem when you instantiate overdouble
, but it could be a problem for other types. You should be usingstd::copy
.If you were using
std::vector
, andretGrid
still needed to return aT*
, it would bereturn &grid[0];
.The assignment operator is broken. It leaks any previous memory, and if the
new
fails, it leaves the object in an inconsistent state. (Having to check for self-assignment is usually a hint that something is wrong.) A correct assignment operator will do all operations which might fail before changing anything in the object. You might search for information about the swap idiom, but something like the following would also work:
.
SomeClass&
SomeClass<T>::operator=( SomeClass const& other )
{
T* newGrid = new T[other.size_total];
std::copy( other.grid, other.grid + other.size_total, newGrid );
delete [] grid;
size_x = other.size_x;
size_y = other.size_y;
size_total = other.size_total;
grid = newGrid;
return *this;
}
You might want to optimize this if the size_total
are equal
(or size_total <= other.size_total
).
And of course, if you use std::vector
, the compiler generated
assignment operator and copy constructor are sufficient; you
don't have to write anything.
Is there any reason why you use a pointer for
I1
? (Or is this just an artifact of a larger context from which you extracted the code?)Concerning
someFunction( &I2.retGrid() );
,someClass::retGrid()
returns a pointer. Regardless of whether you call the function through a pointer or an object. Taking the address results in aT**
.Concerning the last call, there is nothing that would cause a problem here in the code you've shown us. As long as
I2
is in scope and hasn't deleted it's object, there should be no problem. This is the correct way of calling the function on an object. The problem in your code is elsewhere.
I'm not sure of the runtime error that you are getting but status = someFunction(&I2.retGrid());
is passing pointer to a temporary object.
The runtime error could be because of missing copy constructor that is invoked in someClass<double> I2 = *I1;
Thank you to everyone for the extremely useful comments, especially those by AAT, David Rodriguez - dribeas, James Kanze, and Marius Bancila.
It turns out the problem was improper interface with a third-party function inside someFunction
. I found and fixed the error after some sleep and now the call:
status = someFunction(I2.retGrid()); // Compiler gives no error but function returns error value in status
works correctly.
But this discussion brought other very important related issues to light, namely the memory management issues related to my copy constructor and assignment operator and suggested use of vectors. I believe this thread has a lot of value on those merits.
精彩评论