I have a problem understanding the concept of vectors working with move semantics and objects. I'll put the code and explain you the problem exactly, because I really want to understand the logic behind this.
#include <iostream>
#include <vector>
#include <cstring>
#include "Mystring.h"
using namespace std;
class Mystring {
private:
char* str;
public:
Mystring();
Mystring(const char* s);
Mystring(const Mystring& source);
Mystring(Mystring&& source);
~Mystring();
Mystring& operator=(const Mystring& rhs);
Mystring& operator=(Mystring&& rhs);
void display() const;
int get_lenght() const;
const char* get_str() const;
};
int main() {
vector <Mystring> stooges_vec;
stooges_vec.push_back("Larry"); //11
stooges_vec.push_back("Moe"); //12
stooges_vec.push_back("Curly"); //13
return 0;
}
//One-args constructor
Mystring::Mystring(const char* s)
: str{ nullptr } {
if (s == nullptr) {
str = new char[1];
*str = '\0';
}
else {
str = new char[std::strlen(s) + 1];
std::strcpy(str, s);
}
}
//Copy constructor
Mystring::Mystring(const Mystring& source)
:str{ nullptr } {
str = new char[std::strlen(source.str) + 1];
std::strcpy(str, source.str);
}
//Move constructor
Mystring::Mystring(Mystring&& source)
:str{ source.str } {
source.str = nullptr;
std::cout << "Move constructor called." << std::endl;
}
//Destructor
Mystring::~Mystring() {
delete[] str;
}
//Copy assignment
Mystring& Mystring::operator=(const Mystring& rhs) {
std::cout << "Copy assignment called." << std::endl;
if (this == &rhs)
return *this;
delete[] str;
str = new char[std::strlen(rhs.str) + 1];
std::strcpy(str, rhs.str);
return *this;
}
//Move assignment
Mystring& Mystring::operator=(Mystring&& rhs) {
std::cout << "Move assignment called." << std::endl;
if (this == &rhs)
return *this;
delete[] str;
str = rhs.str;
rhs.str = nullptr;
return *this;
}
So, the problem is that I don't understand the process of this program. I took the debugger and everything is fine:
For the first object that I want to add, the "one args constructor" is called first so I create my object, then "the move constructor" gets called so I steal the data and null the pointer of the original object. I finish it, push my object into the vector and I destroy the original object that now sits with a null pointer in it.
Here I get lost for good, because the process looks the same except after that line std::cout << "Move constructor called." << std::endl; the control goes to the copy constructor and I notice (I hope I'm not wrong) that he does a copy of Larry.
The problem is: I have no clue why the compiler wants to make a copy of Larry. It's there, so the compiler can't simply create Moe, use the move semantics and just push Moe at the back of the vector? Why does it have to make a copy?
Plus, a very strange behavior for me is that I see a destructor called after that copy constructor of Larry because I see it being destroyed. I mean, what is the compiler destroying at this point? The original Larry so it keeps the copy, or what's exactly happening there?
Can someone explain to me what is exactly happening in that second push_back()?
Actually if you run your code on ideone:
Live example
You would only see 3 move ctors called:
Move constructor called.
Move constructor called.
Move constructor called.
But that depends on particular implementation of std::vector used in your compiler. Yours seem to reallocate space after first element inserted and before the second is added, so std::vector has to copy first object to the new place using copy ctor and destroying old one. It could not move that object due to the fact you did not make move ctor noexcept. Additional details can be found here:
How to enforce move semantics when a vector grows?
To validate that this is reason simply add a line after your vector construction:
vector <Mystring> stooges_vec;
stooges_vec.reserve( 3 );
stooges_vec.push_back("Larry"); //11
stooges_vec.push_back("Moe"); //12
stooges_vec.push_back("Curly"); //13
this will prevent reallocation btw object insertion. And of course to fix your issue completely make your move ctor and assignment operator noexcept
Related
I have this code, taken from here by the way http://www.cplusplus.com/doc/tutorial/classes2/
// move constructor/assignment
#include <iostream>
#include <string>
#include <utility>
using namespace std;
class Example6
{
string* ptr;
public:
Example6(const string& str) :
ptr(new string(str))
{
cout << "DONT MOVE " << '\n';
}
~Example6()
{
delete ptr;
}
// move constructor
Example6(Example6&& x) :
ptr(x.ptr)
{
cout << "MOVE " << '\n';
x.ptr = nullptr;
}
// move assignment
Example6& operator=(Example6&& x)
{
delete ptr;
ptr = x.ptr;
x.ptr = nullptr;
return *this;
}
// access content:
const string& content() const
{
return *ptr;
}
// addition:
Example6 operator+(const Example6& rhs)
{
return Example6(content() + rhs.content());
}
};
int main()
{
Example6 foo("Exam");
Example6 bar = Example6("ple"); // move-construction
foo = foo + bar; // move-assignment
cout << "foo's content: " << foo.content() << '\n';
return 0;
}
I only added output in constructor to see which is being called. To my surprise it is always the first one, copy constructor. Why does it happen? I did some research and found some info about elision. Is it somehow possible to prevent it and always call move constructor?
Also, as a side note, as I said this code is from cplusplus.com. However, I read about move semantics in some other places and I wonder if this move constructor here is done right. Shouldn't it call
ptr(move(x.ptr))
instead of just
ptr(x.ptr)
The way I understand this, if we use the second option, then we are calling copy constructor of string, instead of move, because x is rvalue reference that has a name, so it is really lvalue and we need to use move to cast it to be rvalue. Do i miss something, or is it really tutorial's mistake?
Btw, adding move doesn't solve my first problem.
So anything with a name is an lvalue.
An rvalue reference with a name is an lvalue.
An rvalue reference will bind to rvalues, but it itself is an lvalue.
So x in ptr(x.ptr) is an rvalue reference, but it has a name, so it is an lvalue.
To treat it as an rvalue, you need to do ptr( std::move(x).ptr ).
Of course, this is mostly useless, as moving a ptr does nothing as ptr is a dumb raw pointer.
You should be following the rule of 0 here.
class Example6 {
std::unique_ptr<string> ptr;
public:
Example6 (string str) : ptr(std::make_unique<string>(std::move(str))) {cout << "DONT MOVE " << '\n';}
Example6():Example6("") {}
~Example6 () = default;
// move constructor
Example6 (Example6&& x) = default;
// move assignment
Example6& operator= (Example6&& x) = default;
// access content:
const string& content() const {
if (!ptr) *this=Example6{};
return *ptr;
}
// addition:
Example6 operator+(const Example6& rhs) {
return Example6(content()+rhs.content());
}
};
because business logic and lifetime management don't belong intermixed in the same class.
While we are at it:
// addition:
Example6& operator+=(const Example6& rhs) & {
if (!ptr) *this = Example6{};
*ptr += rhs.content();
return *this;
}
// addition:
friend Example6 operator+(Example6 lhs, const Example6& rhs) {
lhs += rhs;
return lhs;
}
Copy constructor is called ... - why?
The premise of your question is faulty: The copy constructor is not called. In fact, the class is not copyable.
The first constructor is a converting constructor from std::string. The converting constructor is called because Example6 objects are initialised with a string argument. Once in each of these expressions:
Example6 foo("Exam")
Example6("ple")
Example6(content() + rhs.content()
... instead of move constructor
There are a few copy-initialisations by move in the program. However, all of them can be elided by the compiler.
Is it somehow possible to prevent it and always call move constructor?
There are a few mistakes that can prevent copy elision. For example, if you wrote the addition operator like this:
return std::move(Example6(content()+rhs.content()));
The compiler would fail to elide the move and probably tell you about it if you're lucky:
warning: moving a temporary object prevents copy elision
Shouldn't it call
ptr(move(x.ptr))
instead of just
ptr(x.ptr)
There's no need. Moving a pointer is exactly the same as copying a pointer. Same holds for all fundamental types.
The way I understand this, if we use the second option, then we are calling copy constructor of string, instead of move
ptr is not a string. It is a pointer to a string. Copying a pointer does nothing to the pointed object.
PS. The example program is quite bad quality. There should never be owning bare pointers in C++.
I can say your class does not have a copy constructor.
Because copy ctor parameter have to be const and reference
class Example6{
public:
Example6(const Example6 &r);
};
I saw some code that reconsruct Object on c++.
from GeeksForGeeks :
#include<iostream>
#include<string.h>
using namespace std;
class String
{
char *p;
int len;
public:
String(const char *a);
};
String::String(const char *a)
{
int length = strlen(a);
p = new char[length +1];
strcpy(p, a);
cout << "Constructor Called " << endl;
}
int main()
{
String s1("Geeks"); // line 1
const char *name = "forGeeks";
s1 = name; // line 3
return 0;
}
Now, after line 3 s1 is a different object?
I always thought that after you construct object, you can't dereference it.
What you see is an assignment.
A copy assignment operator is automatically generated in your class, since you don't define one yourself. From the page I linked to:
If no user-defined copy assignment operators are provided for a class
type (struct, class, or union), the compiler will always declare one
as an inline public member of the class.
There is actually no dereferencing on line 3: there is only a replacement of an object with an another. The line 3 calls the constructor of String and assign the object to s1. So two different instances of String are being created but the constructor is not ´recalled´ on the first object but assign the created one to s1. The operator = is used as its default behavior which is to find if there is a constructor that matches the type given to the = operator.
On a side note, the dynamic memory is not freed at any point in the code making it a bad code sample.
Let's break it down.
s1 = name;
First, the compiler will construct a new String object using the constructor String(char const*). Then it uses the copy assignment operator to update s1 with this newly created object.
Since you did not define this operator, it simply does a copy of the class members, i.e. p and len: that's the default implementation the compiler generates for you. The old object is not cleaned up before so you are leaking memory... You should thus write your copy semantics using the copy and swap idiom:
class String {
char *p;
int len;
public:
// Your constructors...
// Swap function
friend void swap(String& s1, String& s2) {
using std::swap;
swap(s1.p, s2.p);
swap(s1.len, s2.len);
}
String(String const& other) : p(new char[other.len]()), len(other.len) {
std::copy(other.p, other.p + len, p);
}
String& operator=(String other) {
swap(*this, other);
return *this;
}
// And finally, a destructor:
/* virtual */ ~String() {
delete [] p;
}
};
When I remove The Destructor from code the output come as i desired but if i free the space manually program become mad :( Please some one help me, I'm Using Code::Blocks IDE and Running in Linux mint OS
#include<iostream>
#include<cstring>
using namespace std;
class str
{
char *p;
int len;
public:
str() {
len=0;
p=NULL;
}
str(const char *s);
str(const str &s);
~str() {
cout<<" Distructor Called ";
delete p;
}
friend str operator+(const str &s,const str &t);
friend bool operator<=(const str &s,const str &t);
friend void show(str &s);
};
str::str(const char *s)
{
len=strlen(s);
p=new char[len+1];
strcpy(p,s);
}
str::str(const str &s)
{
len=s.len;
p=new char[len+1];
strcpy(p,s.p);
}
void show(str &s)
{
cout<<s.p;
}
str operator+(const str &s,const str &t)
{
str tem;
tem.len=s.len+t.len;
tem.p=new char[tem.len+1];
strcpy(tem.p,s.p);
strcat(tem.p,t.p);
return tem;
}
bool operator<=(const str &s,const str &t)
{
if(s.len<=t.len)
return true;
else
return false;
}
int main()
{
str s1="New ";
str s2="York";
str s3="Delhi";
str string1,string2,string3;
string1=s1;
string2=s1+s2;
string3=s1+s3;
cout<<"\nString1 = ";
show(string1);
cout<<"\nString2 = ";
show(string2);
cout<<"\nString3 = ";
show(string3);
cout<<"\n\n";
if(string1<=string2) {
show(string1);
cout<<" Smaller Than ";
show(string2);
cout<<"\n";
} else {
show(string3);
cout<<"Smaller Than ";
show(string1);
cout<<"\n";
}
return 0;
}
Read about the Rule of Three.
When you don't declare the assignment operator, a default one is generated by the compiler which does the following:
Assign all the object's members from the corresponding members of the assignment operator's argument, calling the copy assignment operators of the object's class-type members, and doing a plain assignment of all non-class type (e.g. int or pointer) data members.
First of all, the above bold text applies to char *p in your class.
In your operator+ function, tem is an object on stack. When the function ends, tem goes out of scope, and its destructor is called.
So what happens is string1's p is assigned tem's p as per the default assignment operator generated by the compiler, meaning string1's p points to the same memory location as tem's p, which was deallocated after it went out of scope! Hence, string1 does not have the expected value. Later, when string1 goes out of scope and its destructor is called, delete is called on the same memory location for the second time, hence leading to the error shown. Similarly, for string2.
Things will be fine if you overload the assignment operator like this:
void str::operator=(const str&s) {
delete[] p;
len=s.len;
p=new char[len+1];
strcpy(p,s.p);
}
In this case, tem's p will be copied over before its destructor is called.
NOTE:
It works when you remove the destructor because the default
destructor generated by the compiler does not deallocate the
allocated memory, but this will leak memory, which is BAD.
There is another major flaw in your code. Use delete[] for
deallocating arrays.
You have not overloaded the assignment operator. Because of this the same pointer getting assigned and getting deleted twice which is causing the exception.
If I override operator= will the copy constructor automatically use the new operator? Similarly, if I define a copy constructor, will operator= automatically 'inherit' the behavior from the copy constructor?
No, they are different operators.
The copy constructor is for creating a new object. It copies an existing object to a newly constructed object.The copy constructor is used to initialize a new instance from an old
instance. It is not necessarily called when passing variables by value into functions
or as return values out of functions.
The assignment operator is to deal with an already existing object. The assignment operator is used to change an existing instance to have
the same values as the rvalue, which means that the instance has to be
destroyed and re-initialized if it has internal dynamic memory.
Useful link :
Copy Constructors, Assignment Operators, and More
Copy constructor and = operator overload in C++: is a common function possible?
No. Unless you define a copy ctor, a default will be generated (if needed). Unless you define an operator=, a default will be generated (if needed). They do not use each other, and you can change them independently.
No. They are different objects.
If your concern is code duplication between copy constructor and assignment operator, consider the following idiom, named copy and swap :
struct MyClass
{
MyClass(const MyClass&); // Implement copy logic here
void swap(MyClass&) throw(); // Implement a lightweight swap here (eg. swap pointers)
MyClass& operator=(MyClass x)
{
x.swap(*this);
return *this;
}
};
This way, the operator= will use the copy constructor to build a new object, which will get exchanged with *this and released (with the old this inside) at function exit.
No.
And definitely have a look at the rule of three
(or rule of five when taking rvalues into account)
Consider the following C++ program.
Note: My "Vector" class not the one from the standard library.
My "Vector" class interface:
#include <iostream>
class Vector {
private:
double* elem; // elem points to an array of sz doubles
int sz;
public:
Vector(int s); // constructor: acquire resources
~Vector() { delete[] elem; } // destructor: release resources
Vector(const Vector& a); // copy constructor
Vector& operator=(const Vector& a); // copy assignment operator
double& operator[](int i){ return elem[i]; };
int size() const {return sz;};
};
My "Vector" class members implementation:
Vector::Vector(int s) // non-default constructor
{
std::cout << "non-default constructor"<<std::endl;
elem = {new double[s]};
sz =s;
for (int i=0; i!=s; ++i) // initialize elements
elem[i]=0;
}
Vector::Vector(const Vector& a) // copy constructor
:elem{new double[a.sz]},
sz{a.sz}
{
std::cout << "copy constructor"<<std::endl;
for (int i=0; i!=sz; ++i) // copy elements
elem[i] = a.elem[i];
}
Vector& Vector::operator=(const Vector& a) // copy assignment operator
{
std::cout << "copy assignment operator"<<std::endl;
double* p = new double[a.sz];
for (int i=0; i!=a.sz; ++i)
p[i] = a.elem[i];
delete[] elem; // delete old elements
elem = p;
sz = a.sz;
return *this;
}
int main(){
Vector v1(1);
v1[0] = 1024; // call non-default constructor
Vector v2 = v1; // call copy constructor !!!!
v2[0] = 1025;
std::cout << "v2[0]=" << v2[0] << std::endl;
Vector v3{10}; // call non-default constructor
std::cout << "v3[0]=" << v3[0] << std::endl;
v3 = v2; // call copy assignment operator !!!!
std::cout << "v3[0]=" << v3[0] << std::endl;
}
Then, the program output:
non-default constructor
copy constructor
v2[0]=1025
non-default constructor
v3[0]=0
copy assignment operator
v3[0]=1025
To wrap up:
Vector v2 = v1; lead to call copy constructor.
v3 = v2; lead to call copy assignment operator.
In case 2, Object v3 already exists (We have done: Vector v3{10};). There are two obvious differences between copy constructor and copy assignment operator.
copy constructor NO NEED to delete old elements, it just copy construct a new object. (as it Vector v2)
copy constructor NO NEED to return the this pointer.(Furthermore, all the constructor does not return a value).
No, they are not the same operator.
I just got a seg fault in overloading the assignment operator for a class FeatureRandomCounts, which has _rects as its pointer member pointing to an array of FeatureCount and size rhs._dim, and whose other date members are non-pointers:
FeatureRandomCounts & FeatureRandomCounts::operator=(const FeatureRandomCounts &rhs)
{
if (_rects) delete [] _rects;
*this = rhs; // segment fault
_rects = new FeatureCount [rhs._dim];
for (int i = 0; i < rhs._dim; i++)
{
_rects[i]=rhs._rects[i];
}
return *this;
}
Does someone have some clue? Thanks and regards!
*this = rhs;
calls operator=(), which is the function you are writing. Cue infinite recursion, stack overflow, crash.
Also, if you used a std::vector rather than a C-style array, you probably would not need to implement operator=() at all.
As mentioned, you have infinite recursion; however, to add to that, here's a foolproof way to implement op=:
struct T {
T(T const& other);
T& operator=(T copy) {
swap(*this, copy);
return *this;
}
friend void swap(T& a, T& b);
};
Write a correct copy ctor and swap, and exception safety and all edge cases are handled for you!
The copy parameter is passed by value and then changed. Any resources which the current instance must destroy are handled when copy is destroyed. This follows current recommendations and handles self-assignment cleanly.
#include <algorithm>
#include <iostream>
struct ConcreteExample {
int* p;
std::string s;
ConcreteExample(int n, char const* s) : p(new int(n)), s(s) {}
ConcreteExample(ConcreteExample const& other)
: p(new int(*other.p)), s(other.s) {}
~ConcreteExample() { delete p; }
ConcreteExample& operator=(ConcreteExample copy) {
swap(*this, copy);
return *this;
}
friend void swap(ConcreteExample& a, ConcreteExample& b) {
using std::swap;
//using boost::swap; // if available
swap(a.p, b.p); // uses ADL (when p has a different type), the whole reason
swap(a.s, b.s); // this 'method' is not really a member (so it can be used
// the same way)
}
};
int main() {
ConcreteExample a (3, "a"), b (5, "b");
std::cout << a.s << *a.p << ' ' << b.s << *b.p << '\n';
a = b;
std::cout << a.s << *a.p << ' ' << b.s << *b.p << '\n';
return 0;
}
Notice it works with either manually managed members (p) or RAII/SBRM-style members (s).
*this = rhs; // segment fault
This is definitively not the way to do it. You call = recursively, not calling the built in assignment operator. Assign variables one by one. Don't be lazy.
The following line:
*this = rhs; // segment fault
will recursively call your operator=() function resulting in a stack overflow.
You should probably replace it with straight-forward assignments of the various member fields.
As Neil said, using something like std::vector<> will remove much of the responsibility away from your code. If for whatever reason you can't or don't want to use std::vector<>, you might also want to consider using the 'swap idiom' for your assignment operator. This will make the function exception safe (if the allocation of the memory for FeatureCount array fails and throws an exception, the original object that's being assigned to will be left unchanged). Something like the following:
void FeatureRandomCounts::swap( FeatureRandomCounts& other)
{
FeatureCount* tmp_rects = other._rects;
int tmp_dim = other._dim; // or whatever type _dim is
// similarly for other members of FeatureRandomCounts...
// now copy the other contents to
this->_rects = other._rects;
this->_dim = other._dim;
// assign other members of rhs to lhs
other._rects = tmp_rects;
other._dim = tmp_dim;
// etc.
return;
}
Now your assignment can look like:
FeatureRandomCounts & FeatureRandomCounts::operator=(const FeatureRandomCounts &rhs)
{
FeatureRandomCounts tmp( rhs); // make a copy
tmp.swap( *this); // swap the contents of the copy and *this
return *this;
// the contents of tmp (which has the old
// stuff that was in *this) gets destructed
}
Note that you need a proper copy constructor for this to work, but given the Big 3 rule you already need a proper copy ctor.