Operator equals overloading c++ apparently not working - c++

Can someone explain me why the cout doesn't get called here?
#include <iostream>
using namespace std;
class Test {
public:
int a = 1;
Test &operator=(const Test &other) {
cout << "overloading here" << endl;
return *this;
}
};
int main() {
Test t1;
Test &t3 = t1;
return 0;
}

Test &t3 = t1;
is creating a reference for Test and initiaizing that. = operator is not used there.
There are no cout other than inside operator=, so cout won't be called.
Note that
Test t3 = t1;
(without &) will also not call operator= because this is an initialization of object and a constructor is used for that.
To use operator=, you should do like
Test t3;
t3 = t1;

Related

NRVO with and without default copy constructor

I make a demo code like below:
#include <iostream>
using namespace std;
class Test {
public:
Test();
~Test();
Test(const Test&);
};
Test::Test(const Test& rhs) {
cout << "do copy constructor function" << endl;
}
Test::Test()
{
cout << "do constructor function" << endl;
}
Test::~Test() {
cout << "do deconstructor function" << endl;
}
Test fun1() {
Test tmp;
return tmp; // NRVO
}
int main() {
Test t1 = fun1();
return 0;
}
I run it under VS2017 release mode, and c++ standard is set to c++17.
The result is as expected, copy constructor has not been run.
But when I comment out the self-defined copy constructor and run it again:
#include <iostream>
using namespace std;
class Test {
public:
Test();
~Test();
};
Test::Test()
{
cout << "do constructor function" << endl;
}
Test::~Test() {
cout << "do deconstructor function" << endl;
}
Test fun1() {
Test tmp;
return tmp; // NRVO
}
int main() {
Test t1 = fun1();
return 0;
}
It turns out NRVO is invalid, because constructor runs once while deconstructor runs twice.
So why would that be?

why the constructor is getting called here?

I have question on the below code. In the main function what will happen when the line: obj = 20; is executed. I am not able to understand why it calls the constructor? Could anyone of you please explain?
#include <iostream>
#include <string>
using namespace std;
class Int {
int x;
public:
Int(int x_in = 0)
: x{ x_in }
{
cout << "Conversion Ctor called" << endl;
}
operator string()
{
cout << "Conversion Operator" << endl;
return to_string(x);
}
};
int main()
{
Int obj(3);
string str = obj;
obj = 20;
string str2 = static_cast<string>(obj);
obj = static_cast<Int>(30);
return 0;
}
Within the class there is not defined the assignment operator operator =( int ). But the class has the conversion constructor
Int(int x_in = 0)
So in this statement
obj = 20;
there is called the constructor like Int( 20 ) to convert the integer value to an object of the type Int that can be assigned to the object obj due to the implicit move assignment operator generated by the compiler.
Lacking a assignment operator taking int, your compiler uses the next best assignment operator it can get it's hands on, which is the implicitly declared move assignment operator (Int::operator=(Int&&)). To use this operator, a rvalue reference to a Int object is required, which the compiler creates using the constructor Int::Int(int), i.e. the compiler treats obj = 20; as
obj.operator=(Int(20));
To see what's happening here, you could implement the move assignment operator yourself to print something to the console when the assignment operator is executed:
class Int
{
...
public:
...
Int& operator=(Int&& other)
{
std::cout << "move assignment of Int, new value: " << other.x << '\n';
x = other.x;
return *this;
}
// the following members are only declared to make sure the available
// constructors/operators are the same as in the original version of the code
Int(Int&&) = default;
Int& operator=(Int const&) = default;
Int(Int const&) = default;
};

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.

Return class destructor

I'm a student and I'm learning C++. I quite good at C++ still "simple" things get me entangled. I've recently learn classes, methods, constructor/deconstructor, inheritance, virtual, etc. I have this code :
#include <iostream>
using namespace std;
class test {
int a, c;
public:
test() { cout << "Constructor\n"; }
test(int a) :a(a) { cout<<"Explicit Constructor\n"; }
test foo(const test&, const test&);
~test() { cout << "DECONSTRUCTOR!\n"; }
};
test test::foo(const test &t1, const test &t2) {
test rez;
rez.c = t1.a + t2.a;
return rez;
}
void main() {
test t1(5), t2(21), rez;
rez.foo(t1, t2);
cin.ignore();
}
I know that in foo I create a local class that is deleted when out of scope. So when foo is called I should see one constructor and one destructor, but it gives me one more deconstructor, so I have one constructor for two destructors.
Add one more method to your class test:
test(test const &) { cout << "Copy constructor" << endl; }
See what happens then.
You've forgotten to implement the copy constructor:
test(const test &rhs)
{
cout << "Constructor";
}
It's being used to copy the rez object back to the caller of foo
Refer to comments:
test test::foo(const test &t1, const test &t2) {
test rez;
// constructor for rez invoked
rez.c = t1.a + t2.a;
return rez;
// destructor for rez invoked
}
void main() {
test t1(5), t2(21), rez;
// 3 constructor invocations
rez.foo(t1, t2);
// constructor for return value is called
// but you are missing it since you did not implement copy constructor
// destructor for return value is called
// as you did not use it
cin.ignore();
// 3 destructor invocations
}

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.