C++ strange behaviour with function call by value - c++

I have an object which is a class interface to the matrix struct found in the GNU Scientific Library
typedef double real_t;
typedef unsigned short index_t;
class matrix
{
gsl_matrix* m;
public:
matrix(index_t rows, index_t columns, real_t val);
}
matrix::matrix(index_t rows, index_t columns, real_t val)
{
m=gsl_matrix_alloc(rows,columns);
gsl_matrix_set_all(m, val);
return;
}
index_t matrix::rows(void)
{
return m->size1;
}
index_t matrix::columns(void)
{
return m->size2;
}
the problem is that if I use a function taking the matrix object by value like this one:
void test_function(const matrix m){};
and use it in a program like this one
int main()
{
matrix m(4,4,1);
cout << m.rows() << '\t' << m.columns() << endl;
test_function(m);
cout << m.rows() << '\t' << m.columns() << endl;
}
I surprisingly obtain that the number of rows of the matrix object m is modified by the function test_function to garbage value, even if I put the keyword const before the argument and the call is made by value.
But the strangest thing is that if I use a function that makes use of call by reference like this one:
void test_function(const matrix &m){};
nothing happens and everything seems to be all right.
As far as I know, call by value shouldn't be able to modify the argument of the function,
especially if the function does nothing as in this case and especially if I explicitly use the keyword const before the argument name in the function prototype...
Any help would be greatly appreciated.
EDIT:
I also have defined the copy constructor for the matrix class as
matrix& matrix::operator= (const matrix& src)
{
gsl_matrix_memcpy(m,src.m);
return *this;
}
which does a complete copy (I guess) of the gsl_matrix struct
EDIT:
Ok, I think I've finally understood: the call by value function creates a shallow copy object that just contains the pointer to the real object, when test_function terminates all local variables are destroyed, so the destructor of the matrix class (which is defined but omitted here for the sake of brevity) is called, but in this way the object in the main (to which the local m points to) is destroyed together with the local variable.. Anyway I solved the problem defining a proper copy constructor that performs a complete (deep) copy of the object and then using call by reference which should be better for heavy computation. Thanks a lot to everybody for your help!

because the default copy ctor just makes a shallow copy, that is, it just copies the value of pointer m, instead of allocate a new one.

When you pass an argument by value, compiler calls the one-argument copy constructor to create the value in the function. If you do not provide a copy constructor, compiler generates one for you.
In the example above, compiler generated copy constructor makes shallow copy of the inner matrix pointer. When the newly generated matrix in the function goes out of scope, the shallow copied pointer gets deleted also. My guess is this is the reason.
You could check this by providing a copy constructor which does not copy the gsl_matrix pointer.

Related

I am pretty confused on the mental model for move semantics in C++ [duplicate]

What is it?
What does it do?
When should it be used?
Good links are appreciated.
1. "What is it?"
While std::move() is technically a function - I would say it isn't really a function. It's sort of a converter between ways the compiler considers an expression's value.
2. "What does it do?"
The first thing to note is that std::move() doesn't actually move anything. It changes an expression from being an lvalue (such as a named variable) to being an xvalue. An xvalue tells the compiler:
You can plunder me, move anything I'm holding and use it elsewhere (since I'm going to be destroyed soon anyway)".
in other words, when you use std::move(x), you're allowing the compiler to cannibalize x. Thus if x has, say, its own buffer in memory - after std::move()ing the compiler can have another object own it instead.
You can also move from a prvalue (such as a temporary you're passing around), but this is rarely useful.
3. "When should it be used?"
Another way to ask this question is "What would I cannibalize an existing object's resources for?" well, if you're writing application code, you would probably not be messing around a lot with temporary objects created by the compiler. So mainly you would do this in places like constructors, operator methods, standard-library-algorithm-like functions etc. where objects get created and destroyed automagically a lot. Of course, that's just a rule of thumb.
A typical use is 'moving' resources from one object to another instead of copying. #Guillaume links to this page which has a straightforward short example: swapping two objects with less copying.
template <class T>
swap(T& a, T& b) {
T tmp(a); // we now have two copies of a
a = b; // we now have two copies of b (+ discarded a copy of a)
b = tmp; // we now have two copies of tmp (+ discarded a copy of b)
}
using move allows you to swap the resources instead of copying them around:
template <class T>
swap(T& a, T& b) {
T tmp(std::move(a));
a = std::move(b);
b = std::move(tmp);
}
Think of what happens when T is, say, vector<int> of size n. In the first version you read and write 3*n elements, in the second version you basically read and write just the 3 pointers to the vectors' buffers, plus the 3 buffers' sizes. Of course, class T needs to know how to do the moving; your class should have a move-assignment operator and a move-constructor for class T for this to work.
Wikipedia Page on C++11 R-value references and move constructors
In C++11, in addition to copy constructors, objects can have move constructors.
(And in addition to copy assignment operators, they have move assignment operators.)
The move constructor is used instead of the copy constructor, if the object has type "rvalue-reference" (Type &&).
std::move() is a cast that produces an rvalue-reference to an object, to enable moving from it.
It's a new C++ way to avoid copies. For example, using a move constructor, a std::vector could just copy its internal pointer to data to the new object, leaving the moved object in an moved from state, therefore not copying all the data. This would be C++-valid.
Try googling for move semantics, rvalue, perfect forwarding.
You can use move when you need to "transfer" the content of an object somewhere else, without doing a copy (i.e. the content is not duplicated, that's why it could be used on some non-copyable objects, like a unique_ptr). It's also possible for an object to take the content of a temporary object without doing a copy (and save a lot of time), with std::move.
This link really helped me out :
http://thbecker.net/articles/rvalue_references/section_01.html
I'm sorry if my answer is coming too late, but I was also looking for a good link for the std::move, and I found the links above a little bit "austere".
This puts the emphasis on r-value reference, in which context you should use them, and I think it's more detailed, that's why I wanted to share this link here.
Q: What is std::move?
A: std::move() is a function from the C++ Standard Library for casting to a rvalue reference.
Simplisticly std::move(t) is equivalent to:
static_cast<T&&>(t);
An rvalue is a temporary that does not persist beyond the expression that defines it, such as an intermediate function result which is never stored in a variable.
int a = 3; // 3 is a rvalue, does not exist after expression is evaluated
int b = a; // a is a lvalue, keeps existing after expression is evaluated
An implementation for std::move() is given in N2027: "A Brief Introduction to Rvalue References" as follows:
template <class T>
typename remove_reference<T>::type&&
std::move(T&& a)
{
return a;
}
As you can see, std::move returns T&& no matter if called with a value (T), reference type (T&), or rvalue reference (T&&).
Q: What does it do?
A: As a cast, it does not do anything during runtime. It is only relevant at compile time to tell the compiler that you would like to continue considering the reference as an rvalue.
foo(3 * 5); // obviously, you are calling foo with a temporary (rvalue)
int a = 3 * 5;
foo(a); // how to tell the compiler to treat `a` as an rvalue?
foo(std::move(a)); // will call `foo(int&& a)` rather than `foo(int a)` or `foo(int& a)`
What it does not do:
Make a copy of the argument
Call the copy constructor
Change the argument object
Q: When should it be used?
A: You should use std::move if you want to call functions that support move semantics with an argument which is not an rvalue (temporary expression).
This begs the following follow-up questions for me:
What is move semantics? Move semantics in contrast to copy semantics is a programming technique in which the members of an object are initialized by 'taking over' instead of copying another object's members. Such 'take over' makes only sense with pointers and resource handles, which can be cheaply transferred by copying the pointer or integer handle rather than the underlying data.
What kind of classes and objects support move semantics? It is up to you as a developer to implement move semantics in your own classes if these would benefit from transferring their members instead of copying them. Once you implement move semantics, you will directly benefit from work from many library programmers who have added support for handling classes with move semantics efficiently.
Why can't the compiler figure it out on its own? The compiler cannot just call another overload of a function unless you say so. You must help the compiler choose whether the regular or move version of the function should be called.
In which situations would I want to tell the compiler that it should treat a variable as an rvalue? This will most likely happen in template or library functions, where you know that an intermediate result could be salvaged (rather than allocating a new instance).
std::move itself doesn't really do much. I thought that it called the moved constructor for an object, but it really just performs a type cast (casting an lvalue variable to an rvalue so that the said variable can be passed as an argument to a move constructor or assignment operator).
So std::move is just used as a precursor to using move semantics. Move semantics is essentially an efficient way for dealing with temporary objects.
Consider Object A = B + (C + (D + (E + F)));
This is nice looking code, but E + F produces a temporary object. Then D + temp produces another temporary object and so on. In each normal "+" operator of a class, deep copies occur.
For example
Object Object::operator+ (const Object& rhs) {
Object temp (*this);
// logic for adding
return temp;
}
The creation of the temporary object in this function is useless - these temporary objects will be deleted at the end of the line anyway as they go out of scope.
We can rather use move semantics to "plunder" the temporary objects and do something like
Object& Object::operator+ (Object&& rhs) {
// logic to modify rhs directly
return rhs;
}
This avoids needless deep copies being made. With reference to the example, the only part where deep copying occurs is now E + F. The rest uses move semantics. The move constructor or assignment operator also needs to be implemented to assign the result to A.
"What is it?" and "What does it do?" has been explained above.
I will give a example of "when it should be used".
For example, we have a class with lots of resource like big array in it.
class ResHeavy{ // ResHeavy means heavy resource
public:
ResHeavy(int len=10):_upInt(new int[len]),_len(len){
cout<<"default ctor"<<endl;
}
ResHeavy(const ResHeavy& rhs):_upInt(new int[rhs._len]),_len(rhs._len){
cout<<"copy ctor"<<endl;
}
ResHeavy& operator=(const ResHeavy& rhs){
_upInt.reset(new int[rhs._len]);
_len = rhs._len;
cout<<"operator= ctor"<<endl;
}
ResHeavy(ResHeavy&& rhs){
_upInt = std::move(rhs._upInt);
_len = rhs._len;
rhs._len = 0;
cout<<"move ctor"<<endl;
}
// check array valid
bool is_up_valid(){
return _upInt != nullptr;
}
private:
std::unique_ptr<int[]> _upInt; // heavy array resource
int _len; // length of int array
};
Test code:
void test_std_move2(){
ResHeavy rh; // only one int[]
// operator rh
// after some operator of rh, it becomes no-use
// transform it to other object
ResHeavy rh2 = std::move(rh); // rh becomes invalid
// show rh, rh2 it valid
if(rh.is_up_valid())
cout<<"rh valid"<<endl;
else
cout<<"rh invalid"<<endl;
if(rh2.is_up_valid())
cout<<"rh2 valid"<<endl;
else
cout<<"rh2 invalid"<<endl;
// new ResHeavy object, created by copy ctor
ResHeavy rh3(rh2); // two copy of int[]
if(rh3.is_up_valid())
cout<<"rh3 valid"<<endl;
else
cout<<"rh3 invalid"<<endl;
}
output as below:
default ctor
move ctor
rh invalid
rh2 valid
copy ctor
rh3 valid
We can see that std::move with move constructor makes transform resource easily.
Where else is std::move useful?
std::move can also be useful when sorting an array of elements. Many sorting algorithms (such as selection sort and bubble sort) work by swapping pairs of elements. Previously, we’ve had to resort to copy-semantics to do the swapping. Now we can use move semantics, which is more efficient.
It can also be useful if we want to move the contents managed by one smart pointer to another.
Cited:
https://www.learncpp.com/cpp-tutorial/15-4-stdmove/
std::move itself does nothing rather than a static_cast. According to cppreference.com
It is exactly equivalent to a static_cast to an rvalue reference type.
Thus, it depends on the type of the variable you assign to after the move, if the type has constructors or assign operators that takes a rvalue parameter, it may or may not steal the content of the original variable, so, it may leave the original variable to be in an unspecified state:
Unless otherwise specified, all standard library objects that have been moved from being placed in a valid but unspecified state.
Because there is no special move constructor or move assign operator for built-in literal types such as integers and raw pointers, so, it will be just a simple copy for these types.
Here is a full example, using std::move for a (simple) custom vector
Expected output:
c: [10][11]
copy ctor called
copy of c: [10][11]
move ctor called
moved c: [10][11]
Compile as:
g++ -std=c++2a -O2 -Wall -pedantic foo.cpp
Code:
#include <iostream>
#include <algorithm>
template<class T> class MyVector {
private:
T *data;
size_t maxlen;
size_t currlen;
public:
MyVector<T> () : data (nullptr), maxlen(0), currlen(0) { }
MyVector<T> (int maxlen) : data (new T [maxlen]), maxlen(maxlen), currlen(0) { }
MyVector<T> (const MyVector& o) {
std::cout << "copy ctor called" << std::endl;
data = new T [o.maxlen];
maxlen = o.maxlen;
currlen = o.currlen;
std::copy(o.data, o.data + o.maxlen, data);
}
MyVector<T> (const MyVector<T>&& o) {
std::cout << "move ctor called" << std::endl;
data = o.data;
maxlen = o.maxlen;
currlen = o.currlen;
}
void push_back (const T& i) {
if (currlen >= maxlen) {
maxlen *= 2;
auto newdata = new T [maxlen];
std::copy(data, data + currlen, newdata);
if (data) {
delete[] data;
}
data = newdata;
}
data[currlen++] = i;
}
friend std::ostream& operator<<(std::ostream &os, const MyVector<T>& o) {
auto s = o.data;
auto e = o.data + o.currlen;;
while (s < e) {
os << "[" << *s << "]";
s++;
}
return os;
}
};
int main() {
auto c = new MyVector<int>(1);
c->push_back(10);
c->push_back(11);
std::cout << "c: " << *c << std::endl;
auto d = *c;
std::cout << "copy of c: " << d << std::endl;
auto e = std::move(*c);
delete c;
std::cout << "moved c: " << e << std::endl;
}
std::move simply casts a variable to an rvalue reference. This rvalue reference is notated with &&. Let's say you have a class Foo and you instantiate an object like this
Foo foo = Foo();
If you then write
Foo foo2 = std::move(foo);
that's the same thing as If I wrote
Foo foo2 = (Foo&&) foo;
std::move replaces this cast to an rvalue reference.
The reason why you would want to write either of the previous 2 lines of code
is that if you write
Foo foo2 = foo;
The copy constructor will be called.
Let's say Foo instances have a pointer to some data on the heap which they own.
In Foo's destructor that data on the heap gets deleted.
If you want to distinghuish between copying the data from the heap and taking ownership of that data, you can write a constructor which takes in const Foo& and that constructor can perform the deep copy. Then you can write a constructor which takes in an rvalue reference (Foo&&) and this constructor can simply rewire the pointers.
This constructor which takes in Foo&& will be called when you write
Foo foo2 = std::move(foo);
and when you write
Foo foo2 = (Foo&&) foo;

How to avoid deletion of object given as a parameter in C++

I am new to C++ and I do not know how to solve the following problem.
The class Foo has a constructor which creates a array of doubles of a given size. The destructor deletes this array. The print method prints the array.
#include <iostream>
class Foo {
private:
int size;
double* d;
public:
Foo(int size);
~Foo();
void print();
};
Foo::Foo(int size)
{
this->size = size;
d = new double[size];
for (int i = 0; i < size; i++)
{
d[i] = size * i;
}
}
Foo::~Foo()
{
delete[] d;
}
void Foo::print()
{
for (int i = 0; i < size; i++)
{
std::cout << d[i] << " ";
}
std::cout << std::endl;
}
Now I have a function func(Foo f) which does nothing.
void func(Foo f){}
int main()
{
Foo f(3);
f.print();
func(f);
Foo g(5);
f.print();
return 0;
}
Executing this code gives the following output:
0 3 6
0 5 10
Although I am printing f both times, somehow the values inside the array have changed.
I guess that the destructor of Foo is called on parameter Foo f after the execution of func(Foo f) and this frees the allocated memory for d, which is reallocated for Foo g(5). But how can I avoid this without using vectors or smart pointers?
The problem is with the design of the class. The default copy constructor will create a new instance of Foo when passed by value into the free standing function named func.
When the instance of Foo named f exits scope then the code invokes the user-provided destructor that deletes the array of doubles. This opens the code to the unfortunate situation of deleting the same array twice when the original instance of Foo named f exits scope at the end of the program.
When run on my machine, the code does not produce the same output. Instead I see two output lines of 0 3 6 followed by fault indicating the double free operation.
The solution is to avoid the copy by passing by reference (or by const reference): void func(Foo const &f) { } or to supply a valid copy constructor that makes a deep copy of the underlying array. Passing by reference is just a bandaid that avoids exercising the bug.
Using std::vector<double> fixes the problem because the default copy constructor will perform a deep copy and avoid double deallocation. This is absolutely the best approach in this small example, but it avoids having to understand the root of the problem. Most C++ developers will learn these techniques then promptly do what they can to avoid having to write code that manually allocates and deallocates memory.
You should probably pass the object as a reference func(Foo& f) or - if you do not want to modify it at all - as a constant reference func(const Foo& f). This will not create or delete any objects during the function call.
Aside from that, as others have already mentioned, your class should better implement the Rule of Three.
When you pass a value to a function, it is supposed to be copied. The destructor is run on the copy and should no effect on the original object. Foo fails to implement a copy constructor, so compiler provides the default one which simply performs a member-wise copy of the struct. As a result, the "copy" of Foo inside Func contains the same pointer as the original, and its destructor frees the data pointed to by both.
In order to be usable by idiomatic C++ code, Foo must implement at least a copy constructor and an assignment operator in addition to the destructor. The rule that these three come together is sometimes referred to as "the rule of three", and is mentioned in other answers.
Here is an (untested) example of what the constructors could look like:
Foo::Foo(const Foo& other) {
// copy constructor: construct Foo given another Foo
size = other->size;
d = new double[size];
std::copy(other->d, other->d + size, d);
}
Foo& Foo::operator=(const Foo& other) {
// assignment: reinitialize Foo with another Foo
if (this != &other) {
delete d;
size = other->size;
d = new double[size];
std::copy(other->d, other->d + size, d);
}
return *this;
}
Additionally, you can also modify functions like func to accept a reference to Foo or a constant reference to Foo to avoid unnecessary copying. Doing this alone would also happen fix the immediate problem you are having, but it would not help other issues, so you should definitely implement a proper copy constructor before doing anything else.
It's a good idea to get a good book on C++ where the rule of three and other C++ pitfalls are explained. Also, look into using STL containers such as std::vector as members. Since they implement the rule of three themselves, your class wouldn't need to.
One problem is that calling func creates a bitwise copy. When that copy goes out of scope the destructor is called which deletes your array.
You should change void func(Foo f){} to void func(Foo& f){}.
But better yet you add a create copy constructor or add a private declaration to stop it being called unexpectedly.

C++ Destructor being called in overloaded arithmetic operators

I have a custom-made Matrix library for a neural network program and overloaded arithmetic operators.
Here's the class declarations:
class Matrix{
public:
int m;
int n;
double **mat;
Matrix(int,int);
Matrix(int);
Matrix(const Matrix& that):mat(that.mat),m(that.m),n(that.n)
{
mat = new double*[m];
for(int i = 0;i<m;i++)mat[i] = new double[n];
};
~Matrix();
friend istream& operator>>(istream &in, Matrix &c);
friend ostream& operator<<(ostream &out, Matrix &c);
Matrix operator+(const Matrix& other);
};
This is the function definition for + operation:
Matrix Matrix::operator+(const Matrix& other)
{
Matrix c(m,n);
for(int i=0;i<m;i++)
{
for(int j = 0; j<n;j++)
c.mat[i][j] = mat[i][j] + other.mat[i][j];
}
return c;
}
I have tried to implement it in all ways and error is same...here's an instance
Matrix x(m,n); //m and n are known
x = a+b; // a and b are also m by n matrices
I have debugged the code using breakpoints and here's the error...
The local matrix 'c' in operator function is destroyed before it is returned and hence what is assigned to x is a garbage pointer..
Please suggest me something...
You need to define a copy constructor for your class. The copy constructor will need to allocate memory for mat and make a copy of the data.
Without this, when you return c, a new object is constructed that has the same value of mat as c. When c subsequently goes out of scope, it deletes c.mat. As a result, the copy of c is left with a dangling pointer.
Having done this, you should also implement an assignment operator.
The value you returned is used to initialize a temporary, and this temporary is then copied into the result after the value you returned has been destroyed. This is normal behavior (unless the call is elided because of NRVO).
However, since your class has no explicitly defined copy constructor, the implicitly generated one will be invoked, and that will just copy a pointer (mat) to stuff that has been deallocated by the returned object's destructor.
This is a violation of the so-called Rule of Three, a programming best-practice saying that whenever your class explicitly defines a copy-constructor, an assignment operator, or a destructor, then it should define all of them. The rationale is that a class that defines one of them most likely does so because it is managing some resource, and for correctly handling resource releasing/acquiring logic, all of those three special member functions are needed.
Notice that in C++11 you can also have a move constructor that will be allowed to perform the transfer of the Matrix's content just by assigning pointers and invalidating the object you moved from.
Matrix(Matrix&& m)
{
mat = m.mat;
m.mat = nullptr;
}
Of course, if you introduce a move constructor, you will have to modify your class destructor accordingly to check if you really have to release the allocated memory:
~Matrix()
{
if (m.mat == nullptr)
{
return;
}
...
}
Your Matrix class has a raw pointer member and presumably allocates memory in its constructors, yet you have no copy constructor or copy assignment operator.
Also, you have a destructor, yet you have no copy constructor or copy assignment operator. This is a violation of the Rule of Three.
Your Matrix c is a local variable. So it is destroyed when that method where it was created ends. In C++ this unwanted situation is usually solved by copying objects. You can define copy constructor and assignment operator = with the same functionality. The problem of copying is that it is slow, so if you want it to be faster, you should use a different approach withotu copying. For example, you can add a parameter to the method where caller would pass a reference to the existing matrix object where to store the result.
You need a copy constructor and an assignment operator for your class that make a deep copy of the object as the compiler generated functions will not.
The compiler generated copy constructors and assignment operators will simply copy the objects contained in the class. In your case these are PODs, so the automatically generated functions will simply do a bitwise copy. In the case of the double**, this will result in the copy of the pointer value, not the pointed to values. As a result, you end up with two Matrix objects pointing to the same underlying data, just before the destructor pulls the rug out from underneath you.
You should change your code to return a Matrix *, rather than a Matrix object. This way you can ensure that the Matrix object lives after the function. (Your current code makes the Matrix object a function variable, thus it will be removed after the function has ended).
Your code could look like this:
Matrix *Matrix::operator+(const Matrix& other)
{
Matrix *c = new Matrix(m,n);
for(int i=0;i<m;i++)
{
for(int j = 0; j<n;j++)
c->mat[i][j] = mat[i][j] + other.mat[i][j];
}
return c;
}
EDIT: Apparently this is bad practice, guess I also learned something today :)

why do we have to send the const " type " reference instead of just the types name to the constructor

i m trying to make a simple program ( & yes , it is a homework ) that can generate Dates , & like most of normal people : i made my Class attributes private , i tried to send the same type that i m working on to the constructor but the complier have not accept it , i did some research & i found out that in cases like that people generously send a const "type" reference to the constructor witch meant to me that have not understand OOP well
so why do we have to send the const " type " reference instead of just the types name to the constructor ? & please give me some links or websites for beginners
here is a peace of my Code :
class Date {
int d ;
int m ;
int y ;
public :
Date();
Date(int , int , int);
Date(const Date &);// my question is : why do we have to write this instead of Date( Date )
};
PS : sorry for my English
To paraphrase our question:
why do we have to write Date(const Date &) instead of Date(Date)?
I'm going to split this into two parts, the first answering why a copy constructor needs to take its argument per reference, the second why this needs to be a const reference.
The reason a copy constructor needs to take its argument per reference is that, for a function that's taking an argument per copy void f(T arg), when you call it f(obj), obj is copied into arg using T's copy constructor. So if you want to implement the copy constructor, you'd better not take the argument by copy, because this would call the copy constructor while invoking it, leading to an endless recursion. You can easily try this yourself:
struct tester {
tester(tester) {std::cout << "inside of erroneous copy ctor\n";}
};
int main()
{
tester t1;
std::cout << "about to call erroneous copy ctor\n";
tester t2(t1);
std::cout << "done with call erroneous copy ctor\n";
return 0;
}
That program should only ever write one line and then blow the stack.
Note: As Dennis points out in his comment, actually this program is not guaranteed to compile, so, depending on your compiler, you might not really be able to try it.
Bottom line: A copy constructor should take its argument by reference, because taking it per copy would require the copy constructor.
That leaves the question of why it is const T& and not simply T&? In fact, there are two reasons for that.
The logical reason is that, when you invoke the copy constructor, you do not expect the object copied from to change. In C++, if you want to express that something is immutable, you use const. This tells users that they can safely pass their precious objects to your copy constructor, because it won't do anything with it except read from it. As a nice side effect, if you implement the copy constructor and accidentally try to write to the object, the compiler throws an error message at you, reminding you of the promise made to the caller.
The other reason is that you cannot bind temporary objects to non-const references, you can only bind them to const references. A temporary object is, for example, what a function might return:
struct tester {
tester(tester& rhs) {std::cout << "inside of erroneous copy ctor\n";}
};
tester void f()
{
tester t;
return t;
}
When f() is called, a tester object is created inside, and a copy of it is then returned to the caller, which might then put it into another copy:
tester my_t = f(); // won't compile
The problem is that f() returns a temporary object, and in order to call the copy constructor, this temporary would need to bind to the rhs argument of tester's copy constructor, which is a non-const reference. But you cannot bind a temporary object to a non-const reference, so that code won't compile.
While you can work around this if you want (just don't copy the temporary, but bind it to a const reference instead, which extends the temporary's lifetime to the end of the reference's lifetime: const tester& my_t = f()), people expect to be able to copy temporaries of your type.
Bottom line: A copy constructor should take its argument by const reference, because otherwise users might not be willing or able to use it.
Here's one more fact: In the next C++ standard, you can overload functions for temporary objects, so-called rvalues. So you can have a special copy constructor that takes temporary objects overloading the "normal" copy constructor. If you have a compiler that already supports this new feature, you can try it out:
struct tester {
tester(const tester& rhs) { std::cout << "common copy ctor\n"; }
tester( tester&& rhs) { std::cout << "copy ctor for rvalues\n"; }
};
When you use the above code to invoke our f()
tester my_t = f();
the new copy constructor for rvalues should be called when the temporary object returned by the call to f() is copied to my_t and the regular copy constructor might be called in order to copy the t object from inside of f() to the returned temporary. (Note: you might have to disable your compiler's optimization in order to see this, as the compiler is allowed to optimize away all the copying.)
So what can you with this? Well, when you copy an rvalue, you know that the object copied from is going to be destroyed after the call to the copy constructor, so the copy constructor taking an rvalue (T&&) could just steal the values from the argument instead of copying them. Since the object is going to be destroyed anyway, nobody is going to notice.
For some classes (for example, for string classes), moving the value from one object to another could be much cheaper than copying them.
if I understood your question correctly, to avoid making copies/calling constructor of object.
void function(const T&); // does not create new T
void function(T&); // does not create newT, however T must be modifiable (lvalue)
void function(T); // creates new T
for simple types creating new copy is trivial (and often optimized away by compiler).
For complex object, creating new copy may be very expensive.
Hence you pass it by reference.
https://isocpp.org/wiki/faq/references
https://isocpp.org/wiki/faq/ctors
if you are asking why can not do the following:
struct type {
type(type);
};
Is because this would lead to infinite recursion, since constructor depends on itself
you can do this however
struct type {
type(type, int);
};
since this constructor is different from synthesized type(const type&)
http://en.wikipedia.org/wiki/Copy_constructor
In addition to #aaa's answer, I will try to answer the const part. The const part simply means that the object you are passing logically does not change. This makes sense, because when a copy constructor is called with a Date object argument d, d should not be modified at all!
You can remove the const and your code will still work the same way. However, const provides the additional security that you can never modify the variable marked as const. In your case, this means you can not call any of the non-const method of Date. This is enforced by the compiler at compile-time.
Historically this is the reason for introducing references to the language. Here's an explanation:
In C you can pass values to parameters by value (void f(struct custom_type i)) or by pointer (void g(struct custom_type* i)).
With POD values (int, char, etc.) passing by value is not a problem, but if you are looking at complex structures, then the stack grows too quickly by placing entire structures on stack for function calls. That is why in C you tend to pass structures as parameters by pointer, even if the function doesn't modify them.
In C++ there are cases where neither option worked:
passing by pointers involves a counter-intuitive syntax for operators (if you define operator + for a class custom_type writing custom_type a, b, c; a = &b + &c; is counterintuitive as a doesn't get assigned the sum of the addresses. Furthermore if you wanted to be able to assign the sum of the values to a and the sum of the addresses to a, you would have to somehow differentiate between the cases, by syntax).
passing by value is impossible or undesired in the case of copy constructors. In your case, if you have Date(Date d) {} and assignment Date a; Date b(a); what you get is that a copy of a is created just to be passed as a parameter to the constructor of b. This leads to infinite recursion, as creating a copy of a to pass as a parameter involves is the same as Date d = a; b = Date(d);.
For these reasons (and there may have been others) a decision was made to create references: data types that looks syntactically like a value type, but behave like pointers (that is, it points to the value of another variable, but you access it like a variable, not like a pointer).
Regarding the reason why you need const in the declaration, it is so that your constructor will accept temporary objects. As you cannot modify the value of a temporary references if your constructor doesn't accept const& you can only use the copy constructor for non-const stable objects.
That is, if you have:
class Date
{
public:
Date(Date& other); // non-const reference
...
you can write:
Date a;
Date b = a;
but not:
Date someFunction() { return Date(xxx); }
Date a = someFunction(); // someFunction returns a temporary object
neither:
const Date someImportantDate;
Date a = someImportantDate; // cannot pass const value to non-const

Is it a good practice to pass struct object as parameter to a function in c++?

I tried an example live below:
typedef struct point
{
int x;
int y;
} point;
void cp(point p)
{
cout<<p.x<<endl;
cout<<p.y<<endl;
}
int main()
{
point p1;
p1.x=1;
p1.y=2;
cp(p1);
}
The result thats printed out is:
1
2
which is what I expected. My question is: Does parameter p get the full copy of object p1? If so, I wonder if this is a good practice? (I assumed when the struct gets big in size, this will create a lot of copy overhead).
Yes, there's nothing wrong with this and yes, p is a complete copy of p1. I wouldn't call a struct with two ints large, but if the struct does get large and if you don't need to alter it in the function body you could consider passing it by const reference:
void cp(const point& p)
{
// ...
}
There is nothing wrong with passing structs as parameters. Everything is passed by value in C++ so a full copy is indeed made.
The struct you gave in your example is small so it's probably not a problem if you pass it by value. But if you work with bigger data structures, you may want to pass it by reference.
Beware though, passing by reference means that if you modify the struct inside your function, your original struct will be modified. Be sure to use the const keyword in every case where you don't modify the struct. This will give you an immediate information about if your functions do modify the information or not.
Your example could be modified to work with references this way :
typedef struct point
{
int x;
int y;
} point;
void cp(const point& p) // You can know that cp doesn't modify your struct
{
cout<<p.x<<endl;
cout<<p.y<<endl;
}
void mod_point(point& p) // You can know that mod_points modifies your struct
{
p.x += 1;
p.y += 1;
}
int main()
{
point p1;
p1.x=1;
p1.y=2;
cp(p1);
mod_point(p1);
cp(p1); // will output 2 and 3
}
Before I give an answer to your question (you find it at the end of this post), here's a brief summary of the possibilities you have for passing arguments to a function:
1. Copy-constructed objects (pass-by-value):
void cp(point p);
This is pass-by-value. A temporary point object is created and copy-constructed from whatever point object you pass into cp. Once execution leaves the cp function, the temporary object is destroyed.
Note that because the function operates on a copy of whatever was passed to the function, you can modify that local point object as much as you want, the original object of the caller will not be affected. There's no way to change this behaviour with pass-by-value parameters.
2. References to objects (pass-by-reference):
void cp(point& p);
This is pass-by-reference. The actual object passed to the function is available (and potentially subject to modification) inside the function. If you don't want the function to be able to change the passed-in object, declare the parameter as const point&:
void cp(const point& p);
3. Pointers to objects (pass-by-reference):
void cp(point* p);
This is also pass-by-reference, with a few subtle differences. One notable difference is that you can pass a null pointer to the function. This is not possible with references, because references must be initialized and cannot be reseated afterwards. -- As with references, if you want to disallow cp from changing the passed-in point, declare it as const:
void cp(const point* p);
Answer to your question:
Pass-by-value parameters are not inherently bad in any way. Of course, there are types for which copy construction is expensive (e.g. very large or complex objects), or where it has certain side effects (e.g. with std::auto_ptr). In these cases, you should be careful. Otherwise, it's just another feature of C++ which has its perfectly reasonable uses.
void cp(point p){
}
gets it by value
void cp(point *p){
}
gets it by reference
Just like any other data variable.
In java the scenario is different. Passing objects always go by reference.
In you case the point struct is passed "by value" meaning, that the whole structure is copied. For big data types, this can indeed be slow.
You can consider passing the object by reference
void cp(point& p) // p may be altered!
or be const reference
void cp(const point& p) // p may not be altered!
See the possibilities to avoid passing objects by value. The object is not just 'copied', but is copy constructed. So, this involves calling copy constructor and the chain of copy constructors if your object is composed or derived of more objects. Pass by reference if possible.
void cp(point &p const) const
{
cout << p.x << endl;
cout << p.y << endl;
}