About constructor and assignment operator in C++ - c++

I am learning cpp, and now have a problem. Here are the code and the result.
What I very want to know are why there is only one constructor between "1" and "2" and why there are assignment and constructor between "3" and "4".
Thank you in advance
the result
#include <iostream>
#include <string>
using namespace std;
class A{
public:
A(){
cout << "Empty-constructor" << endl;
}
A(const A &a){
cout << "Copy-constructor" << endl;
this->v = a.v;
}
A operator=(const A &a){
cout << "Assignment" << endl;
this->v = a.v;
return *this;
}
int get(){
return v;
}
void set(int v){
this->v = v;
}
private:
int v;
};
A func(){
A a;
return a;
}
int main(){
cout << "1" << endl;
A b = func();
cout << "2" << endl;
A c;
cout << "3" << endl;
c = b;
cout << "4" << endl;
return 0;
}

A b = func(); only produces construction due to copy elision/NRVO; the new A is constructed directly into the caller's memory.
b = c; involves both assignment and construction because you wrote your assignment operator incorrectly, having it return by value, rather than by reference, so after the assignment is performed, it copy-constructs from the object you've just assigned to, returning the copy (which isn't used, and gets thrown away immediately). That's a giant waste, and should really be fixed to make the assignment operator return by reference, changing:
A operator=(const A &a){
to:
A& operator=(const A &a){
Even better, use the copy-and-swap idiom to avoid duplicating code for copying/swapping all over the place.

Related

Change constructor order of operations?

I have the following sample code, and the copy assignment is doing something I don't want - it's first constructing the new samp(6), then copying it to z, then destroying the new samp(6) it built. Is there a way to change the constructors such that = acts like a pointer, destroying the originally constructed samp(5) and replacing it with the new samp(6), calling the destructor on samp(5), rather than samp(6)?
#include <iostream>
class samp
{
public:
samp(int a)
{
m_a = a;
std::cout << "cons" <<m_a << std::endl;
}
int m_a;
samp(const samp& other)
{
std::cout << "copy" << m_a << std::endl;
m_a = other.m_a;
}
samp& operator= (const samp& other)
{
std::cout << "assg" << m_a << std::endl;
samp* z =new samp(other.m_a);
return *z;
}
~samp()
{
std::cout << "dest" <<m_a<< std::endl;
}
};
int main()
{
samp z(5);
z = samp(6);
std::cout << z.m_a << std::endl;
return 0;
}
Maybe pointer semantics is what you want:
#include <memory>
// ...
auto z = std::make_unique<samp>(5);
z = std::make_unique<samp>(6); // dest5
std::cout << z->m_a << '\n'; // 6
Although if you are coming to C++ from a language where object names are object references, it may be better to get used to C++ value semantics instead of trying to replicate object references :)
operator= is a member of your object so the this pointer is available. Also, assignment means the target of the assignment should change. Your version is creating a new object, but leaving the target alone. See if this does what you want:
samp& operator= (const samp& other)
{
m_a = other.m_a;
return *this;
}

Parenthesis vs curly braces

#include<iostream>
using namespace std;
class test
{
public:
int a,b;
test()
{
cout<<"default construictor";
}
test(int x,int y):a(x),b(y){
cout<<"parmetrized constructor";
}
};
int main()
{
test t;
cout<<t.a;
//t=(2,3);->gives error
t={2,3}; //calls paramterized constructor
cout<<t.a;
}
Output:- default construictor4196576parmetrized constructor2
why in case of the above example, parameterized constructor(even though default constructor is already called.) is called in case of {} and not in ()
I added some additional code to show what is actually happening.
#include<iostream>
using namespace std;
class test
{
public:
int a,b;
test()
{
cout << "default constructor" << endl;
}
~test()
{
cout << "destructor" << endl;
}
test(int x,int y):a(x),b(y)
{
cout << "parameterized constructor" << endl;
}
test& operator=(const test& rhs)
{
a = rhs.a;
b = rhs.b;
cout << "assignment operator" << endl;
return *this;
}
};
int main()
{
test t;
cout << t.a << endl;
//t=(2,3);->gives error
t={2,3}; //calls parameterized constructor
cout << t.a << endl;
}
Output:
default constructor
4197760
parameterized constructor
assignment operator
destructor
2
destructor
So the statement t={2,3}; is actually constructing a new test object using the parameterized constructor, calling the assignment operator to set t to be equal to the new, temporary test object, and then destroying the temporary test object. It's equivalent to the statement t=test(2,3).
use
test t(2,3);
instead of
test t;
t=(2,3);
Because parentheses are used after object declaration.

How values of member variables are getting copied even though copy constructor is not getting called due to RVO in C++

I am not able to understand how the values of member variables are getting copied even though the constructor is not getting called in the below program.
#include <iostream>
using namespace std;
class myclass
{
public:
int x;
int y;
myclass(int a, int b)
{
cout << "In Constructor" << endl;
x = a;
y = b;
}
~myclass()
{
cout << "In Destructor" << endl;
}
myclass(const myclass &obj)
{
cout << "In Copy Constuctor " << obj.x << " " << obj.y << endl;
x = obj.x;
y = obj.y;
}
myclass &operator=(const myclass &obj)
{
cout << "In Operator Overloading" << obj.x << obj.y << endl;
x = obj.x;
y = obj.y;
return *this;
}
};
int main()
{
myclass obj1 = myclass(2, 3);
cout << "obj1.x : " << obj1.x << "obj1.y" << obj1.y << endl;
}
Output:
In Constructor
obj1.x : 2obj1.y3
In Destructor
I understood that due to Return Value Optimization, copy constructor is not getting called. But I didn't understand how obj1 is getting values 2 and 3. Can any one please help me to understand this or how Return Value Optimization will work behind the scenes.
The values aren't getting copied, because they don't need to be copied. Instead, the values which would have been copied are initialized in place. Copy elision means that the compiler essentially turns this:
myclass obj1 = myclass(2, 3);
Into this:
myclass obj1(2, 3);
So no extra object is constructed which needs to be copied.
Note that RVO, which you've referred to, is a form of copy elision. But this specific case of copy elision is not RVO.

Immutable objects in C++11 and move semantics

I'm trying to create an immutable class in C++11, which (for convenience) provides methods that modify the current state. To satisfy the immutable contract, these methods must return a new instance with the updated contents.
In the following snippet, ImmutableObject::setA() and ImmutableObject::setB() update the a and b attributes in a new instance of ImmutableObject created via a copy constructor and return it. The method print() just displays the content.
int main(int argc, char **argv) {
ImmutableObject().setA(1).setB(2).print();
return 0;
}
(An implementation of ImmutableObject is provided below.)
Implementing this naively using C++03 causes an unnecessary copies to be created.
I am wondering if C++11 move semantics can be used to exploit the fact that ImmutableObject() and the return value of setA() are just temporary copies that could be directly updated.
I already added a rvalue copy and assignment constructor in the code below, but it does not look like it's working. When running the program, I get
In constructor
In copy constructor
In copy constructor
a=1, b=2
Any advice would be greatly appreciated!
#include <iostream>
using namespace std;
class ImmutableObject {
public:
ImmutableObject() {
std::cout << "In constructor" << std::endl;
}
ImmutableObject(const ImmutableObject &obj) : a(obj.a), b(obj.b) {
std::cout << "In copy constructor" << std::endl;
}
ImmutableObject(ImmutableObject&& other) {
std::cout << "In move constructor" << std::endl;
a = other.a;
b = other.b;
}
ImmutableObject &operator=(ImmutableObject&& other) {
if (this != &other) {
a = other.a;
b = other.b;
}
return *this;
}
ImmutableObject setA(int value) {
ImmutableObject result(*this);
result.a = value;
return result;
}
ImmutableObject setB(int value) {
ImmutableObject result(*this);
result.b = value;
return result;
}
void print() {
std::cout << "a=" << a << ", b=" << b << std::endl;
}
private:
int a;
int b;
};

What happens if a rvalue reference goes out of scope?

I'm experimenting with move semantics and I am wondering what happens if a rvalue refernece goes out of scope. With following code i get runtime problems if I std::move an lvalue into
function(T t) with t = std::move(lvalue) --> SEGFAULT OR double free
but not into
function(T &&t) with t = std::move(lvalue) --> OK
Does anybody know why?
Also, if you swap the two code-blocks in main() you get a different runtime error 0_o
// Compile with:
// g++ move_mini.cpp -std=c++11 -o move_mini
#include <iostream>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <utility>
using namespace std;
int num_copied;
class T{
public:
T() : a(nullptr), b(nullptr){};
T(const T &t) : a(new string(*t.a)),
b(new string(*t.b)){
num_copied++;
};
T(T &&t){
*this = move(t);
};
T(string s1, string s2){
this->a = new string(s1);
this->b = new string(s2);
};
~T(){
delete this->a;
delete this->b;
};
T& operator=(const T &lhs){
num_copied++;
delete this->a;
delete this->b;
this->a = new string(*lhs.a);
this->b = new string(*lhs.b);
return *this;
};
T& operator=(T &&lhs){
swap(this->a, lhs.a);
swap(this->b, lhs.b);
return *this;
};
string *a;
string *b;
};
void modify1(T t){
}
void modify3(T &&t){
}
int main(){
cout << "##### modify1(T t) #####" << endl;
T t_mv1("e", "asdsa");
num_copied = 0;
modify1(move(t_mv1));
cout << "t = move(t_mv) copies " << num_copied << " times." << endl;
cout << endl;
cout << "##### modify3(T &&t) #####" << endl;
T t_mv3("e", "aseeferf");
num_copied = 0;
modify3(move(t_mv3));
cout << "t = move(t_mv) copies " << num_copied << " times." << endl;
cout << endl;
return 0;
}
Let's start here:
modify1(move(t_mv1));
For constructing the parameter of modify1, the move constructor of T is used:
T(T &&t){
*this = move(t); // <--- this calls move assignment operator
};
Note the commented line above. By that time, the two data members of *this object are default initialized, which for pointers means they're left with an indeterminate value. Next, the move assignment operator is called:
T& operator=(T &&lhs){
swap(this->a, lhs.a); // reads indeterminate values and invokes
swap(this->b, lhs.b); // undefined behaviour
return *this;
};
Now when modify1 returns, the parameter object gets destroyed and the destructor of T calls delete on uninitialized pointers, again invoking undefined behaviour
I haven't looked in the second part (with modify3), but I suspect something similar is going on.