I have the following code:
#include <iostream>
using namespace std;
class A
{
public:
A ()
{
cout<<"cons"<<endl;
}
~A ()
{
cout<<"dest"<<endl;
}
};
A
gokul (void)
{
A p;
return p;
}
int
main ()
{
A a = gokul();
cout<<"done\n";
}
When I run it. I get the following output:
cons
done
dest
I was expecting the output to be:
cons --> p created,
cons --> for a, gokul returning
dest --> p destroyed, gokul returned
done
dest --> a destroyed, main returned
as the local variable "p" will be destroyed when the function gokul returns and the new variable "a" will be created, no? I have compiled with all three standards 03, 11 & 14 and I get the same result.
~
Your code failed to trace the copy constructor. You need to do this to get a better picture of when an object is created.
#include <iostream>
using namespace std;
class A
{
public:
A() { cout<<"cons"<<endl; }
~A() { cout<<"dest"<<endl; }
A(const &A) { cout << "copy constructed" << endl;}
};
A gokul (void)
{
A p;
return p;
}
int main ()
{
A a = gokul();
cout<<"done\n";
}
When you run this code in Visual Studio 2015, no optimizations, the output is as follows:
cons
copy constructed
dest
done
dest
When you change to release mode, the output is now this:
cons
done
dest
The reason for the second output to not have copy construction is due to the Named Return Value Optimization that is done to remove the copy.
However the point is that you really can't predict how many times a copy constructor is called. As you can see, the unoptimized version also works correctly.
If you have optimization turned on then you get this output because of compiler optimization Copy Elision enter link description here
Or if you run your program in debug mode (without optimization), you don't simply trace copy constructor that is used as #PaulMcKenzie explained.
You can try compile without optimization: /Od (in visual studio), -O0 in gcc. Or just run in debug mode. And then back to optimization version by /O2 or -O2.
Beware that from C++11 compiler can use move constructor if you swith to non optimized version /0d and still you don't get output. So you need to trace move constructor
A(const A&& o){
cout << "move ctor" << endl;
}
Related
This question already has answers here:
This code is undefined, why is it running? How do i crash it?
(7 answers)
Closed 3 years ago.
I'm studying on c++ copy constructor. I wrote the code that uses a shallow copy constructor that causes run-time errors for study. What I intended was making run-time error.
#include <iostream>
#include <cstring>
using namespace std;
class Person{ //define Person class
char *name;
int id;
public:
Person(int id, const char *name);//constructer
~Person();//distructer
Person(Person& p){
this->name = p.name;
this->id = p.id;
}
void changeName(const char *name);
void show(){
cout << id <<',' << name <<endl;
}
};
Person::Person(int id, const char *name){
this -> id = id;
int len = strlen(name);
this->name = new char[len+1];
strcpy(this->name,name);
}
Person::~Person(){
if(name)
delete []name;
}
void Person::changeName(const char *name){
if(strlen(name) > strlen(this->name))
return;
strcpy(this->name,name);
}
int main(){
Person father(1,"Kitae");
Person daughter(father);
cout << "after object daughter maked ---" << endl;
father.show();
daughter.show();
daughter.changeName("Grace");
cout <<"daughter name changed ---"<<endl;
father.show();
daughter.show();
return 0;
}
when I complied on windows 10(compiled by visual studio 2017) and run it, then it works well (run-time error occurs) but it doesn't work well on linux(compiled by g++ 7.3.0)(run-time error doesn't occurs). linux show no error occurs.
So, I debug that code on linux. I used gdb.
after object daughter maked ---
1,Kitae
1,Kitae
daughter name changed ---
1,Grace
1,Grace
[Inferior 1 (process 3297) exited normally]
Is it okay to use shallow copy constructor like that code? if it isn't, why windows and linux are show different result?
The shallow copy only causes the bug indirectly. What actually causes the error is that the pointer gets deleted twice, because of the shallow copy. And the error that happens because of a double-delete depends on the compiler and the operating system.
If you want to catch errors like that on linux, there's a really good tool called valgrind. Just run:
$ valgrind ./my_program
And it will tell you if there are any memory leaks or double-frees.
Or use the address sanitizer mentioned by another commenter when compiling your program. (It does make the code run slower, but that's ok for a debug build).
That being said, not all shallow copies are bad, and there are perfectly valid (and safe) use cases of a shallow copy constructor. For example, suppose I'm writing my own shared_ptr class. The implementation is pretty simple:
template<class T>
class shared_ptr {
T* ptr;
int* counter;
void increment_counter() {
int& count = *counter;
++count;
}
int decriment_counter() {
int& count = *counter;
return --count;
}
public:
explicit shared_ptr(T* ptr)
: ptr(ptr)
, counter(new int(1))
{}
// Make shallow copy, but increment the counter
shared_ptr(shared_ptr const& other)
: ptr(other.ptr)
, counter(other.counter)
{
increment_counter();
}
~shared_ptr() {
int new_count = decriment_counter();
// Only delete the pointer if this was the last copy
if(new_count == 0) {
delete ptr;
delete counter;
}
}
};
Here, shared_ptr keeps track of how many instances of that pointer there are, and it only deletes the pointer if it was the last copy.
By default, gcc doesn't try to detect run time errors caused by undefined behaviour. If you want this, there is a set of add-ons called sanitizers that can help you. Try -fsanitize=address or -fsanitize=undefined compiler flags.
Live Demo
class test
{
int a;
static int cnt;
public:
test()
{
a=0;
cout <<++cnt;
}
test( int p)
{
a=p;
cout <<++cnt;
}
~test()
{
cout<<cnt--;
}
};
int test::cnt;
void main()
{
test ob,ob1(10);
ob = test();
test();
}
In this code snippet ob=test(); how ob can be assigned a function test.Test is a class and we are invoking it like a function.How can this be possible
Functions return void, objects, references or pointers to objects, that can generally be assigned to variables in your program. In this particular case, you are calling the test class object constructors and possibly encountering undefined behavior on the final call. I need to investigate further on the possible UB, the C++ standard has changed twice since I last read it, VS-2017 may not be the best oracle, and my C++ foo is little weak.
As far as I recall, there's more than one way to initialize an object in C++ and your instructor has obviously given you an assignment to learn this first hand.
test ob; // Invokes default constructor on test
test ob(); // Invokes default constructor on test.
test ob = test::test(); // Invokes default constructor on test.
It's always good to experiment with your code and get it to output usable diagnostics. I tweaked it a bit to get more organized output and force the app to wait on user input prior to exiting. You should also learn to use your debugger. You can learn a lot by simply stepping through your own code.
#include <iostream>
#include <cstdlib>
class test
{
int a;
static int cnt;
public:
test()
{
a = 0;
cnt++;
std::cout << cnt << std::endl;
}
test(int p)
{
a = p;
cnt++;
std::cout << cnt << std::endl;
}
~test()
{
std::cout << cnt << std::endl;
cnt--;
}
};
int test::cnt = 0;
int main(void)
{
{
test ob; // test::cnt is incremented, 1 is displayed on the console.
test ob1(10); // test::cnt is incremented, 2 is displayed on the console.
ob = test::test(); // test::cnt is incremented, 3 is displayed on the console.
// The following instantiates a temporary test object,
// the constructor is called, but test::cnt is not incremented on my system.
// Seems we might be in undefined behavior territory?
test::test();
}
system("pause");
}
Notice that I added an additional context to 'main()'. You can't rely on destructors outputting anything to the console after the end of 'main()' which is where your objects are destructed. Moving all the objects into the additional {} context, forces them to be constructed and destroyed therein, allowing us to a complete picture of what's going on at our console output.
The output on my Windows box for the above code is:
1
2
3
3
3
3
2
1
Press any key to continue . . .
I expected a count to 4 and then a count down from 4. That last call is definitely confusing me. If nobody chimes in with a definitive explanation, I'll look into it as soon as I can.
I have tried this on MSVC versions 19.10.25019.0 and 19.11.25547.0 in debug and release builds and get the same result.
The following program prints 0 1 2 3 4 5.
I expected either
no output, or
a combination of move or copy constructor calls with the destructor calls.
Instead, it seems the destructor was called for 6 of the 7 elements and no copy or move constructor calls were made.
#include <iostream>
struct MyChar {
MyChar(char c) : c{c}{}
MyChar() = default;
MyChar(const MyChar&) { std::cout << "copy"; };
MyChar& operator=(const MyChar&) { std::cout << "assign"; return *this; };
MyChar(MyChar&&) { std::cout << "move"; };
MyChar& operator=(const MyChar&&) { std::cout << "move assign"; return *this; };
~MyChar() { std::cout << cnt++ << '\t'; }
char c{'H'};
static int cnt;
};
int MyChar::cnt{};
int main()
{
auto arr1 = new MyChar[7]{'D'};
}
Why is the destructor being called 6 times without delete (or without the compiler initializing via a copy or a move)?
In playing around with this in the VC compiler, and inspecting the generated assembly code, this appears to be a compiler bug related to the MyChar() = default constructor. Replacing it with MyChar() { } gets rid of the unexpected destructor calls.
By adding some additional code into the destructor, the objects being destroyed are the default-initialized members of arr1. Adding in a call to delete [] arr1, along with including the address of the object being destroyed in the destructor, shows that the first element is destroyed once, while the other 6 are destroyed twice - once when arr1 is constructed, and again when the delete call is made.
This should be reported to Microsoft. It occurs in both VC2015 and VC2017.
I have the following code:
#include <iostream>
#include <memory>
#include <vector>
class Test
{
public:
Test() {}
~Test() { std::cerr << "Delete\n"; }
};
std::vector<std::shared_ptr<Test>> makeList()
{
std::vector<std::shared_ptr<Test>> list;
list.push_back(std::make_shared<Test>(Test()));
return std::move(list);
}
int main(int argc ,char **argv)
{
std::vector<std::shared_ptr<Test>> list;
std::cerr << "Before\n";
list = makeList();
std::cerr << "After\n";
return 0;
}
Which I compile with:
clang++ -std=c++14 -o ptr ptr.cpp
The output is:
Before
Delete
After
Delete
My question is: why is there an object being deleted in the makeList function? My assumption was that the list from the function would be moved into list from main and that therefore no object would be deleted/recreated in the process?
Can this be avoided (as obviously this code is not optimum)?
2 Changes:
std::vector<std::shared_ptr<Test>> makeList()
{
std::vector<std::shared_ptr<Test>> list;
// make_shared does not need a copy of an object, just constructor arguments
list.push_back(std::make_shared<Test>());
// return std::move(list) will defeat RVO. Never do that.
return list;
}
So, the important part is :
list.push_back(std::make_shared<Test>(Test()));
->
list.push_back(std::make_shared<Test>());
Just for clarification because I had the same today, and I had trouble seeing the difference.
list.push_back(std::make_shared<Test>(Test()));
Here temporary is created with Test(). Then copy c-tor for Test is invoked and temporary is destroyed. It is the first destructor call.
The second destructor call appears in the end of the program when list is destroyed.
The right form to avoid temporary creation is:
list.push_back(std::make_shared<Test>());
Besides you shouldn't use std::move returning the value because compiler cannot apply Return value optimisation in such case.
The line list.push_back(std::make_shared<Test>(Test())); makes a temporary Test and then moves it into the actual Test constructed by std::make_shared<T>. This temporary is then destroyed.
std::make_shared<T> requires the arguments to be used in construction of T. For a default constructed T simply provide no arguments.
The correct use, in this case, is this:
list.push_back(std::make_shared<Test>());
I was witnessing some unexpected behavior in a C++ application I am writing in Linux Ubuntu. I would construct an object with parameters and then put a copy of that object into a std::map using the assignment operator. I wrote a simple program to demonstrate this situation...
#include <iostream>
#include <string>
#include <map>
using namespace std;
class Foo
{
public:
Foo(void) : _x(0)
{
cout << "Default" << endl;
}
Foo(int a) : _x(a)
{
cout << "Param" << endl;
}
Foo(Foo const &foo) :
_x(foo._x)
{
cout << "Copy" << endl;
}
Foo& operator=(Foo const &foo)
{
cout << "Assignment" << endl;
if (this != &foo)
{
_x = foo._x;
}
return *this;
}
int get(void)
{
return _x;
}
private:
int _x;
};
int main(int argc, char *argv [])
{
std::map<int, Foo> foos;
Foo a_foo(10);
foos[100] = a_foo;
return 0;
}
Here I am just printing out which constructor/operator gets called in what order so I can see how the construction and assignment works in the "main" function.
When I run this in Windows I get my expected output...
Param
Default
Assignment
When I run this in Linux I get the following output...
Param
Default
Copy
Copy
Assignment
Why are the two extra copy constructors there? It seems very inefficient to create the object so many times?
Thanks!
The answer lies in stl_map.h. Its behaviour depends on whether you compile with C++11 support or not. If you do then the STL can take advantage of move semantics to avoid unnecessary copying. VC++ uses the new language features by default but if you use g++ or clang you need to get used to using the -std=c++0x flag in 4.2 or -std=c++11 in newer versions.
With -std=c++11 set the output with g++4.8 is:
Param
Default
Assignment
Edit: Thank you very much for clarifying for me that my assumption that this was down to move semantics was incorrect. I'm leaving this answer in place to direct users to this better one.