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.
Related
This question already has answers here:
Why is using the move constructor in a return statement legal?
(2 answers)
C++11 rvalues and move semantics confusion (return statement)
(6 answers)
move constructor called on return instead of copy
(1 answer)
Closed 4 months ago.
#include <iostream>
#include <stdio.h>
class Stack{
public:
Stack(int size):msize(size){
std::cout << "Stack::Stack() called" << std::endl;
}
// Constructor 2: Copy constructor with non-const lvalue reference
Stack(Stack &src)
:msize(src.msize)
{
std::cout << "Stack::Stack(const Stack&)" << std::endl;
}
// Constrcutor 3: Move constructor with rvalue reference
Stack(Stack &&src)
:msize(src.msize)
{
std::cout << "Stack::Stack(Stack&&)" << std::endl;
}
~Stack()
{
std::cout << "~Stack()" << std::endl;
}
Stack& operator=(const Stack &src)
{
std::cout << "operator=" << std::endl;
if (this == &src)
return *this;
return *this;
}
Stack& operator=(Stack &&src)
{
std::cout << "operator=&&" << std::endl;
if (this == &src)
return *this;
return *this;
}
int getSize()
{
return msize;
}
private:
int msize;
};
Stack GetStack(Stack &stack)
{
Stack tmp(stack.getSize());
std::cout << "tmp is created" << std::endl;
return tmp;
};
int main(){
Stack s(100);
std::cout << "s is created" << std::endl;
s = GetStack(s);
return 0;
}
I define 3 constructors in Stack class. If I run the main function without any change, then when the tmp object(which is declared in GetStack()) is returned, the compiler will call constructor 3(which is a move constructor) to create a temporary and then return. So under this circumstance, I initially think the ready-to-return object tmp is a rvalue.
But when I comment the code snippet of Constructor 3 and re-run the code, the compiler just calls constructor 2 to create a temporary, so when the move constructor does not exist, tmp is treated as lvalue.
So my question is that is tmp a rvalue or a lvalue when it is ready to return from GetStack(). My compile option is g++ -fno-elide-constructors -o ref ref.cpp
I am starting with C++ environment, I hope I didn't mess up a lot with the concepts here.
I have one task where I need to create an object that has to be able to be copied in two ways, with a shallow copy and a deep copy.
The object has to allow two types of copies. Either the object is shallow copied or deep copy depending on the demands.
In the code below I create a simple example to explain the use case. There, I implement the object Object with a one-argument constructor and a deep copy constructor. Then at some point we use the functions get_shallow and get_deep.
Is there a way to tell the compiler which constructor to use when it copies Object in the scope of the functions? Sometimes I will need a function to return the shallow copy and other times a deep copy.
using namespace std;
class Object;
typedef std::shared_ptr<Object> objectPtr;
class Object{
private:
int *data;
public:
Object(int d);
Object(const Object &source);
~Object();
};
// One argument constructor
Object::Object(int d) {
data = new int;
*data = d;
}
//deep copy constructor
Object::Object(const Object &source) :Object(*source.data) {}
Object::~Object() {delete data;}
Object get_shallow(Object object) {
return object;
}
Object get_deep(Object object) {
return object;
}
int main(){
Object object1 {100};
get_deep(object1); //returns a deep copy of the object
get_shallow(object1); //returns a shallow copy of the object
return 0;
}
You can use a tag on the copy constructor to indicate it is making a shallow copy.
Note carefully that a shallow copy does not own the resources. So when the owner gets destructed, any shallow copy will have dangling pointers. That's a very fragile situation, and easily the source of bugs.
An alternative solution is to have a std::shared_ptr for the shared resources, and then those resources exist until all owners relinquish ownership (typically relinquishing ownership upon destruction, but could be that they change ownership over the lifecycle of the entity object).
Regardless, here's an example of shallow copy constructor.
#include <iostream>
using std::cout;
using std::ostream;
class Object {
int* data;
bool delete_data = true;
public:
enum Shallow_tag { shallow };
~Object();
Object(int d);
Object(Object const&); // DEEP copy.
Object(Object const&, Shallow_tag); // SHALLOW copy.
bool is_shallow() const { return !delete_data; }
auto get_data() const -> int* { return data; }
};
Object::~Object() { if (delete_data) delete data; }
// One argument constructor
Object::Object(int d) : data{new int(d)} { }
//deep copy constructor
Object::Object(Object const& other) : Object(*other.data) {}
// shallow copy
Object::Object(Object const& other, Shallow_tag) : data{other.data}, delete_data{false} {}
Object get_shallow(Object const& object) {
return Object(object, Object::shallow);
}
Object get_deep(Object object) {
return object;
}
ostream& operator<<(ostream& out, Object const& obj) {
out << (obj.is_shallow() ? "shallow" : "deep") << " ";
auto d = obj.get_data();
if (d) out << "data:" << *d;
else out << "data:(null)";
return out;
}
int main() {
auto object1 = Object{100};
auto obj2 = get_deep(object1); //returns a deep copy of the object
auto obj3 = get_shallow(object1); //returns a shallow copy of the object
cout << "obj2 is " << obj2 << "\n";
cout << "obj3 is " << obj3 << "\n";
}
UPDATE: using shared_ptr for the data.
Of course, an int as shared data is probably a bit silly. But for the purposes of an example it is illustrative of whatever the data payload type.
#include <iostream>
#include <memory>
using std::cout;
using std::make_shared;
using std::ostream;
using std::shared_ptr;
class Object {
shared_ptr<int> data;
public:
Object(int d);
auto get_data() const -> int* { return data.get(); }
};
Object::Object(int d) : data{make_shared<int>(d)} { }
auto operator<<(ostream& out, Object const& obj) -> ostream& {
auto d = obj.get_data();
if (d) out << "data:" << *d;
else out << "data:(null)";
return out;
}
Object get_copy(Object o) {
return o;
}
int main() {
auto object1 = Object{100};
auto obj2 = get_copy(object1);
auto obj3 = get_copy(object1);
cout << "obj2 is " << obj2 << "\n"; // 100
cout << "obj3 is " << obj3 << "\n"; // 100
*object1.get_data() = 5;
cout << "obj2 is " << obj2 << "\n"; // 5
cout << "obj3 is " << obj3 << "\n"; // 5
obj2 = Object{75};
cout << "obj2 is " << obj2 << "\n"; // 75
cout << "obj3 is " << obj3 << "\n"; // 5
}
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.
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;
}
The code is following:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct A {
A(int i = -1): i_(i) {
wcout << "Constructor: i = " << i_ << endl;
}
A(A const &a) {
wcout << "Copy constructor: i = " << i_ << " a.i = " << a.i_ << endl;
*this = a;
}
~A() { wcout << "Destructor: i = " << i_ << endl; }
A &operator=(A const& a) {
wcout << "Copy assignment operator: i = " << i_ << " a.i = " << a.i_ << endl;
i_ = a.i_;
return *this;
}
bool operator==(A const& rhs) { return i_ == rhs.i_; }
int get() { return i_; }
private:
int i_;
};
int wmain() {
A a[] = {1, 2, 3, 2, 4, 5};
vector<A> v(a, a + sizeof a/sizeof a[0]);
wcout << "==== Just before remove ====" << endl;
remove(v.begin(), v.end(), 2);
wcout << "==== Just after remove ====" << endl;
return 0;
}
Output:
==== Just before remove ====
Constructor: i = 2
Destructor: i = 2
Constructor: i = 2
Destructor: i = 2
Constructor: i = 2
Destructor: i = 2
Copy assignment operator: i = 2 a.i = 3
Constructor: i = 2
Destructor: i = 2
Constructor: i = 2
Destructor: i = 2
Copy assignment operator: i = 3 a.i = 4
Constructor: i = 2
Destructor: i = 2
Copy assignment operator: i = 2 a.i = 5
==== Just after remove ====
The question is: why destructor is called 6 times while remove() was running? I need this mess to be clarified.
NOTE: execute this code on your system, please, before answering.
NOTE: MSVCPP 11
The question is: why destructor is called 6 times while remove() was
running?
In summary, the destructor calls have to do with the 2 getting implicitly converted to A by remove(). Every time the result of such an implicit conversion goes out of scope, A's destructor gets called.
The reason for those implicit conversions is that remove() needs to compare every element of a to 2. The only way to do this is by calling A::operator==(const A&):
bool operator==(A const& rhs) { ... }
Since rhs is of type const A&, the compiler:
calls A(int) to convert the 2 to an A;
calls operator==(const A&);
calls A::~A() to destroy the temporary.
The latter are the destructor calls that you're seeing.
If you were to add the following comparison operator to A, you'll see those destructor calls disappear:
bool operator==(int rhs) { return i_ == rhs; }
Alternatively, if you were to call remove() like so, you'll see all bar one destructor calls disappear:
remove( v.begin(), v.end(), A(2) );
Finally, if you were to make A::A(int) explicit, the compiler would not allow you to call remove() with 2 as the last argument (you'd have to call it with A(2)).
The question is: why destructor is called 6 times while remove() was
running?
Because std::remove just reorders the elements, but doesn't remove anything from the vector. In the process of reordering, some elements are copy-constructed.
Next code shows in details what happens :
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct A {
A(int i = -1): i_(i) {cout << "I was created" << i_ << endl;}
~A() {
cout << "I was destroyed" << i_ << endl;
}
A(const A& o):i_(o.i_)
{
cout << "I was copied" << i_ << endl;
}
A& operator=(const A& o)
{
i_=o.i_;
cout << "I was assigned" << i_ << endl;
return *this;
}
int get() { return i_; }
bool operator==(A const& rhs) { return i_ == rhs.i_; }
private:
int i_;
};
int main() {
std::cout<<"start"<<std::endl;
A a[] = {1, 2, 3, 2, 4, 5};
std::cout<<"creating"<<std::endl;
vector<A> v(a, a + sizeof a/sizeof a[0]);
std::cout<<"removing"<<std::endl;
remove( v.begin(), v.end(), 2 );
std::cout<<"end"<<std::endl;
}
The remove is placing "removed" elements at the end of the vector.
std::remove is declared as
template<typename ForwardIterator, typename Tp>
ForwardIterator remove(ForwardIterator first, ForwardIterator last,
const Tp& value);
In your usage, the third argument (2) is deduced as int. Because an int variable is not directly comparable against an A object, first a temporary has to be constructed for every
if (*it == value) ...
At the end of the comparison, the temporary is destroyed.
(All in all, you are having a hidden performance problem, resulting from having non-explicit single-argument, a.k.a. conversion constructor.)
Well it's quite simple really; you only need to understand what std::remove does and how it does it. Hint: it does not remove elements from the vector. It moves then around instead, to the back of your collection. Moving an element in vector involves destroying original element. So this is where (part of) your destructor calls is coming from.
The other part is coming from temporary objects - since you passed an int (and not an instance of struct A) as a last parameter to std::remove, an instance of A has to be constructed for the purpose of comparison. If you want to force a little more discipline to your code, try to make it a habit to prefix one-parameter constructors with keyword explicit. It's very efficient at banishing such temporary objects. You will then have to create comparison object explicitly:
remove(v.begin(), v.end(), A(2));