Return class destructor - c++

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
}

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?

c++ missing construction and destruction of an object

The following code:
#include <iostream>
#include <string>
using namespace std;
void print(string a) { cout << a << endl; }
void print(string a, string b) { cout << a << b << endl; }
class A {
public:
string p;
A() { print("default constructor"); }
A(string a){ p = a; print("string constructor ", p); }
A(const A& o) { print("copy constructor"); }
A (A&& o) { print("move constructor"); }
A& operator=(const A& o) { print("copy assignment"); return *this; }
A& operator=(const A&& o) { cout << "move assignment to:" << p << " from:" << o.p << endl; return *this; }
~A() { print("destructor ", p); }
};
A operator+(const A& a, const A& b) {
cout << "add" <<endl;
A c("f");
return c;
}
A f(A& a, A& b, A& c) {
A d("e");
d = a+b+c;
print("after add");
return d;
}
int main() {
A a("a"); A b("b"); A c("c");
A whereDidThisGo {f(a,b,c)};
print("end");
}
has the following output:
string constructor a
string constructor b
string constructor c
string constructor e
add
string constructor f
add
string constructor f
move assignment to:e from:f
destructor f
destructor f
after add
end
destructor e
destructor c
destructor b
destructor a
Process exited after 0.06744 seconds with return value 0
Press any key to continue . . .
Where is the construction/destruction of the whereDidThisGo variable defined in main?
Where is the construction/destruction of the whereDidThisGo variable defined in main?
You do not see the ouptut for this due to named return value optimization(aka NRVO).
it's not a good optimization for people like me who are trying to learn constructors
You can disable this NRVO by providing the -fno-elide-constructors flag to the compiler. Demo.
Also, note that in your example the A::operator=(const A&&) should instead be:
//-----------vv------->no need for const here
A::operator=(A&&)
TIL about NRVO, it's not a good optimization for people like me who are trying to learn constructors haha.
Thank you for the answers, yes the move assignment should be a non-const pointer, I simply overlooked it.

Capturing lambda in std::function results in extra copies

I am trying to write some code which allows me to call a function at some later time by storing the function call and its arguments in a lambda/std::function. Ideally, the arguments would only be copied ONCE (and moved oterhwise) but the smallest number of copies I can achieve seems to be 2.
//==============================================================================
// INCLUDES
//==============================================================================
#include <iostream>
#include <functional>
#include <memory>
//==============================================================================
// VARIABLES
//==============================================================================
static std::unique_ptr<std::function<void()>> queueFunction;
//==============================================================================
// CLASSES
//==============================================================================
class Test {
public:
Test(int a, int b = 20, int c = 30) : _a(a), _b(b), _c(c) {
std::cout << "Test: Constructor" << std::endl;
}
~Test() {
std::cout << "Test: Destructor" << std::endl;
}
Test(const Test& other) :
_a(other._a)
{
std::cout << "Test: Copy Constructor" << std::endl;
}
Test(Test&& other) :
_a(std::move(other._a))
{
std::cout << "Test: Move Constructor" << std::endl;
}
Test& operator=(const Test& other) {
if (this != &other) {
_a = other._a;
std::cout << "Test: Assignment Operator" << std::endl;
}
return *this;
}
Test& operator=(Test&& other) {
if (this != &other) {
_a = std::move(other._a);
std::cout << "Test: Move Assignment Operator" << std::endl;
}
return *this;
}
friend std::ostream& operator<<(std::ostream& os, const Test& v) {
os << "{a=" << v._a << "}";
return os;
}
private:
int _a;
int _b;
int _c;
};
//==============================================================================
// FUNCTIONS
//==============================================================================
void foo(const Test& t);
void _foo(const Test& t);
template <typename F>
void queue(F&& fn) {
std::cout << "queue()" << std::endl;
queueFunction = std::make_unique<std::function<void()>>(std::forward<F>(fn));
}
void dequeue() {
std::cout << "dequeue()" << std::endl;
if (queueFunction) {
(*queueFunction)();
}
queueFunction.reset();
}
void foo(const Test& t) {
std::cout << "foo()" << std::endl;
queue([t](){
_foo(t);
});
//Only a single copy of Test is made here
/*
[t](){
_foo(t);
}();
*/
}
void _foo(const Test& t) {
std::cout << "_foo()" << std::endl;
std::cout << "t=" << t << std::endl;
}
//==============================================================================
// MAIN
//==============================================================================
int main() {
std::cout << "main()" << std::endl;
Test test1(20);
foo(test1);
dequeue();
std::cout << "main() return" << std::endl;
return 0;
}
The output of the above code is:
main()
Test: Constructor
foo()
Test: Copy Constructor
queue()
Test: Copy Constructor
Test: Copy Constructor
Test: Destructor
Test: Destructor
dequeue()
_foo()
t={a=20}
Test: Destructor
main() return
Test: Destructor
Which makes no sense to me. Shouldn't the lambda capture the instance of Test once, then forward that lambda all the way to the new std::function thus causing a move?
If I modify my queue function as such I can at least get rid of once copy.
void queue(std::function<void()> fn) {
std::cout << "queue()" << std::endl;
queueFunction = std::make_unique<std::function<void()>>(std::move(fn));
}
Output:
main()
Test: Constructor
foo()
Test: Copy Constructor
Test: Copy Constructor
queue()
Test: Destructor
dequeue()
_foo()
t={a=20}
Test: Destructor
main() return
Test: Destructor
But I still cannot understand where the extra copy is coming from.
Can someone help to enlighten me?
AFAICT the problem is the const of the foo() argument. When you capture t inside foo(const Test& t), then the type of that capture inside the lambda is also const. Later when you forward the lambda, the lambda's move constructor will have no choice but copy, not move, the capture. You cannot move from const.
After changing foo to foo(Test& t) I get:
main()
Test: Constructor
foo()
Test: Copy Constructor
queue()
Test: Move Constructor
Test: Move Constructor
Test: Destructor
Test: Destructor
dequeue()
_foo()
t={a=20}
Test: Destructor
main() return
Test: Destructor
Alternative solution, mentioned in https://stackoverflow.com/a/31485150/85696, is to use capture in the form [t=t].
With move-capture and two other changes it is possible to eliminate this remaining copy constructor too:
- void foo(const Test& t) {
+ void foo(Test t) {
...
- queue([t](){
+ queue([t = std::move(t)](){
...
- foo(test1);
+ foo(std::move(test1));
main()
Test: Constructor
Test: Move Constructor
foo()
Test: Move Constructor
queue()
Test: Move Constructor
Test: Move Constructor
Test: Destructor
Test: Destructor
Test: Destructor
dequeue()
_foo()
t={a=20}
Test: Destructor
main() return
Test: Destructor

Operator equals overloading c++ apparently not working

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;

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.