Are the following assignment and copy move constructors the most efficient?
if anybody have other way please tell me?
I mean what bout std::swap? and calling assignment through copy constructor is safe in the code below?
#include <iostream>
#include <functional>
#include <algorithm>
#include <utility>
using std::cout;
using std::cin;
using std::endl;
using std::bind;
class Widget
{
public:
Widget(int length)
:length_(length),
data_(new int[length])
{
cout<<__FUNCTION__<<"("<<length<<")"<<endl;
}
~Widget()
{
cout<<endl<<__FUNCTION__<<"()"<<endl;
if (data_)
{
cout<<"deleting source"<<endl;
}
else
{
cout<<"deleting Moved object"<<endl;
}
cout<<endl<<endl;
}
Widget(const Widget& other)
:length_(other.length_),
data_(new int[length_])
{
cout<<__FUNCTION__<<"(const Widget& other)"<<endl;
std::copy(other.data_,other.data_ + length_,data_);
}
Widget(Widget&& other)
/*
:length_(other.length_),
data_(new int[length_])*/
{
cout<<__FUNCTION__<<"(Widget&& other)"<<endl;
length_ = 0;
data_ = nullptr;
std::swap(length_,other.length_);
std::swap(data_,other.data_);
}
Widget& operator = (Widget&& other)
{
cout<<__FUNCTION__<<"(Widget&& other)"<<endl;
std::swap(length_,other.length_);
std::swap(data_,other.data_);
return *this;
}
Widget& operator = (const Widget& other)
{
cout<<__FUNCTION__<<"(const Widget& other)"<<endl;
Widget tem(other);
std::swap(length_,tem.length_);
std::swap(data_,tem.data_);
return *this;
}
int length()
{
return length_;
}
private:
int length_;
int* data_;
};
int main()
{
{
Widget w1(1);
Widget w2(std::move(Widget(2)));
w1 = std::move(w2);
}
cout<<"ENTER"<<endl;
cin.get();
return 0;
}
Looks fine from an efficiency POV, but contains an awful lot of duplicated code. I'd
Implement a swap() operator for your class.
Initialize length_ and data_ where they are declared.
Implement operations in terms of other operations whereever possible.
You might want to use std::memcpy instead of std::copy since you're dealing with a raw array anyway. Some compilers will do that for you, but probably not all of them...
Here's a de-duplicated version of your code. Note how there is only one place which needs to know how two instances of Widget are swapped. And only one place which knows how to allocate a Widget of a given size.
Edit: You usually also want to use argument-dependent lookup to locate swap, just in case you ever have non-primitive members.
Edit: Integrated #Philipp's suggestion of making the assignment operator take it's argument by value. That way, it acts as both move assignment and copy assignment operator. In the move case, not that if you pass a temporary, it won't be copied, since the move constructor, not the copy constructor will be used to pass the argument.
Edit: C++11 allows non-cost members to be called on rvalues for compatibility with previous versions of the standard. This allows weird code like Widget(...) = someWidget to compile. Making operator= require an lvalue for this by putting & after the declaration prevents that. Note though that the code is correct even without that restriction, but it nevertheless seems like a good idea, so I added it.
Edit: As Guillaume Papin pointed out, the destructor should use delete[] instead of plain delete. The C++ standard mandates that memory allocated via new [] be deleted via delete [], i.e. it allows new' andnew []` to use different heaps.
class Widget
{
public:
Widget(int length)
:length_(length)
,data_(new int[length])
{}
~Widget()
{
delete[] data_;
}
Widget(const Widget& other)
:Widget(other.length_)
{
std::copy(other.data_, other.data_ + length_, data_);
}
Widget(Widget&& other)
{
swap(*this, other);
}
Widget& operator= (Widget other) &
{
swap(*this, other);
return *this;
}
int length() const
{
return length_;
}
private:
friend void swap(Widget& a, Widget& b);
int length_ = 0;
int* data_ = nullptr;
};
void swap(Widget& a, Widget& b) {
using std::swap;
swap(a.length_, b.length_);
swap(a.data_, b.data_);
}
The answer is in response to #Abdulrhman's complaint in the comments above that things fail for some (obscure) sequences of assignments. Put into a seperate answer because it's more readable that way.
The complaint was that
Widget w(2);
w = Widget(1) = std::move(w);
crashes. Here's the output I get from
Widget w(2);
w.data()[0] = 0xDEAD; w.data()[1] = 0xBEEF;
w = Widget(1) = std::move(w);
std::cerr << std::hex << w.data()[0] << w.data()[1] << std::endl;
with some code added to Widget to log constructor, destructor and assignment operator calls. Interleaves are comments about where those calls come from
w is constructed
0x7fff619c36c0: [constructor] allocated 2#0x1043dff80
temporary Widget(1) is constructed
0x7fff619c37c0: [constructor] allocated 1#0x1043e0180
first (right) assignment operator argument is constructed. w is empty afterwards!
0x7fff619c3800: [default constructor] empty
0x7fff619c3800: [move constructor] stealing 2#0x1043dff80 from 0x7fff619c36c0, replacing with 0#0x0
first assignment operator does it's job, i.e. moves from by-value argument.
0x7fff619c37c0: [assignment] stealing 2#0x1043dff80 from 0x7fff619c3800, replacing with 1#0x1043e0180
second (left) assignment operator arguments is constructed
0x7fff619c3780: [constructor] allocated 2#0x1043e0280
0x7fff619c3780: [copy constructor] copying 2#0x1043dff80 from 0x7fff619c37c0
second assignment operator does it's job, i.e. moves from by-value argument
0x7fff619c36c0: [assignment] stealing 2#0x1043e0280 from 0x7fff619c3780, replacing with 0#0x0
second assingment operator's by-value argument is destructed
0x7fff619c3780: [destructor] deleting 0#0x0
first assignment operator's by-value argument is destructed
0x7fff619c3800: [destructor] deleting 1#0x1043e0180
temporary created as Widget(1) is destructed.
0x7fff619c37c0: [destructor] deleting 2#0x1043dff80
data contains in "w" after assignments.
deadbeef
finally, "w" is destructed.
0x7fff619c36c0: [destructor] deleting 2#0x1043e0280
I can see no problem there, and compiling this with clang and -faddress-sanitizer, -fcatch-undefined-behaviour doesn't complain either.
Note, though, that the second assigment (the left = operator) copies instead of moving. This is because the first (right) assignment operator returns an lvalue-reference.
You don't need so many swaps and assignments in your move constructor. This:
Widget(Widget&& other) :
length( other.length_ ), data( other.data_ )
{
other.length_ = 0;
other.data_ = nullptr;
}
does the minimum work for the move constructor: 4 assignments in total. Your version had 8, counting the ones in the calls to swap().
Your move assignment is OK, but you might want to consider just writing one operator=() to cover both cases:
Widget &operator=( Widget other ) {
delete data_;
data_ = other.data_;
other.data_ = nullptr;
length_ = other.length_;
other.length_ = 0;
return *this;
}
This is slightly less efficient than your version, in that it can move twice.
Related
I was working on a class with multiple dynamic fields and I was looking for quick way of coding assignment operator.
So let's say I have some basic class Cla, which stores dynamic array of integers (arr) and the size of said array (n).
I've coded this:
Cla::Cla(int* arr, int n) : n(n)
{
this->arr = new int[n];
//allocation error handling
while (--n >= 0)
this->arr[n] = arr[n];
}
Cla::~Cla()
{
delete[] arr;
}
Cla::Cla(const Cla& copy) : Cla(copy.arr, copy.n){}
Cla& Cla::operator= (const Cla& asg)
{
this->~Cla();
*this = asg;
return *this;
}
All of it works properly, except for operator=. The idea was that I'll just destroy my object and then create it again with copying constructor (for the sake of simplicity of the example I don't consider the situation where both objects have the same size and there is no need for deallocation and new allocation). It compiles, but it gives me some nasty errors when executed.
Can you give me some advice on how to correct this code? Is this even possible for it to work this way?
(I know how to write an assingment operator, I'm just asking whether it is possible to do it using destructor and copying constructor. I couldn't find anything like that on the internet.)
Your operator= has undefined behavior. First, you cannot manually call a destructor on an object that was not allocated with placement-new. Second, once an object is destroyed, it cannot be used anymore, which means *this = asg is accessing invalid memory once this->~Cla() has been called, as this is no longer pointing at a valid object. Third, your operator= is running an endless recursion loop, calling itself over and over until the call stack blows up (if you are lucky).
Since you want to use your copy constructor, your operator= would be better served by using the copy-swap idiom instead. Construct a local object to make use of your copy constructor, and then swap the contents of that object with this so that this takes ownership of the copied data and the local object frees the old data when it goes out of scope, eg:
Cla& Cla::operator= (const Cla& asg)
{
if (&asg != this)
{
Cla temp(asg);
std::swap(arr, temp.arr);
std::swap(n, temp.n);
}
return *this;
}
Alternatively:
void Cla::swap(Cla &other)
{
std::swap(arr, other.arr);
std::swap(n, other.n);
}
Cla& Cla::operator= (const Cla& asg)
{
if (&asg != this) {
Cla(asg).swap(*this);
}
return *this;
}
That being said, the fact that your copy constructor is delegating to your converting constructor means that you are using C++11 or later, in which case you should also implement move semantics into your class, not just copy semantics, eg:
Cla::Cla() : arr(nullptr), n(0)
{
}
Cla::Cla(int* arr, int n) : arr(new int[n]), n(n)
{
while (--n >= 0)
this->arr[n] = arr[n];
}
Cla::Cla(Cla &&c) : arr(nullptr), n(0)
{
c.swap(*this);
}
Cla::Cla(const Cla& c) : Cla(c.arr, c.n)
{
}
Cla::~Cla()
{
delete[] arr;
}
void Cla::swap(Cla &other)
{
std::swap(arr, other.arr);
std::swap(n, other.n);
}
Cla& Cla::operator= (Cla asg)
{
asg.swap(*this);
return *this;
}
By passing the asg parameter by value, operator= can decide whether to use copy semantics or move semantics at the call site based on whether an lvalue or rvalue is being passed to it. The compiler will pick the appropriate constructor to construct the asg parameter with, and then this can take ownership of the resulting data.
As explained in this answer, the copy-and-swap idiom is implemented as follows:
class MyClass
{
private:
BigClass data;
UnmovableClass *dataPtr;
public:
MyClass()
: data(), dataPtr(new UnmovableClass) { }
MyClass(const MyClass& other)
: data(other.data), dataPtr(new UnmovableClass(*other.dataPtr)) { }
MyClass(MyClass&& other)
: data(std::move(other.data)), dataPtr(other.dataPtr)
{ other.dataPtr= nullptr; }
~MyClass() { delete dataPtr; }
friend void swap(MyClass& first, MyClass& second)
{
using std::swap;
swap(first.data, other.data);
swap(first.dataPtr, other.dataPtr);
}
MyClass& operator=(MyClass other)
{
swap(*this, other);
return *this;
}
};
By having a value of MyClass as parameter for operator=, the parameter can be constructed by either the copy constructor or the move constructor. You can then safely extract the data from the parameter. This prevents code duplication and assists in exception safety.
The answer mentions you can either swap or move the variables in the temporary. It primarily discusses swapping. However, a swap, if not optimised by the compiler, involves three move operations, and in more complex cases does additional extra work. When all you want, is to move the temporary into the assigned-to object.
Consider this more complex example, involving the observer pattern. In this example, I've written the assignment operator code manually. Emphasis is on the move constructor, assignment operator and swap method:
class MyClass : Observable::IObserver
{
private:
std::shared_ptr<Observable> observable;
public:
MyClass(std::shared_ptr<Observable> observable) : observable(observable){ observable->registerObserver(*this); }
MyClass(const MyClass& other) : observable(other.observable) { observable.registerObserver(*this); }
~MyClass() { if(observable != nullptr) { observable->unregisterObserver(*this); }}
MyClass(MyClass&& other) : observable(std::move(other.observable))
{
observable->unregisterObserver(other);
other.observable.reset(nullptr);
observable->registerObserver(*this);
}
friend void swap(MyClass& first, MyClass& second)
{
//Checks for nullptr and same observable omitted
using std::swap;
swap(first.observable, second.observable);
second.observable->unregisterObserver(first);
first.observable->registerObserver(first);
first.observable->unregisterObserver(second);
second.observable->registerObserver(second);
}
MyClass& operator=(MyClass other)
{
observable->unregisterObserver(*this);
observable = std::move(other.observable);
observable->unregisterObserver(other);
other.observable.reset(nullptr);
observable->registerObserver(*this);
}
}
Clearly, the duplicated part of the code in this manually written assignment operator is identical to that of the move constructor. You could perform a swap in the assignment operator and the behaviour would be right, but it would potentially perform more moves and perform an extra registration (in the swap) and unregistration (in the destructor).
Wouldn't it make much more sense to reuse the move constructor's code in stead?
private:
void performMoveActions(MyClass&& other)
{
observable->unregisterObserver(other);
other.observable.reset(nullptr);
observable->registerObserver(*this);
}
public:
MyClass(MyClass&& other) : observable(std::move(other.observable))
{
performMoveActions(other);
}
MyClass& operator=(MyClass other)
{
observable->unregisterObserver(*this);
observable = std::move(other.observable);
performMoveActions(other);
}
It looks to me like this approach is never inferior to the swap approach. Am I right in thinking that the copy-and-swap idiom would be better off as the copy-and-move idiom in C++11, or did I miss something important?
First of all, it is generally unnecessary to write a swap function in C++11 as long as your class is movable. The default swap will resort to moves:
void swap(T& left, T& right) {
T tmp(std::move(left));
left = std::move(right);
right = std::move(tmp);
}
And that's it, the elements are swapped.
Second, based on this, the Copy-And-Swap actually still holds:
T& T::operator=(T const& left) {
using std::swap;
T tmp(left);
swap(*this, tmp);
return *this;
}
// Let's not forget the move-assignment operator to power down the swap.
T& T::operator=(T&&) = default;
Will either copy and swap (which is a move) or move and swap (which is a move), and should always achieve close to the optimum performance. There might be a couple redundant assignments, but hopefully your compiler will take care of it.
EDIT: this only implements the copy-assignment operator; a separate move-assignment operator is also required, though it can be defaulted, otherwise a stack overflow will occur (move-assignment and swap calling each other indefinitely).
Give each special member the tender loving care it deserves, and try to default them as much as possible:
class MyClass
{
private:
BigClass data;
std::unique_ptr<UnmovableClass> dataPtr;
public:
MyClass() = default;
~MyClass() = default;
MyClass(const MyClass& other)
: data(other.data)
, dataPtr(other.dataPtr ? new UnmovableClass(*other.dataPtr)
: nullptr)
{ }
MyClass& operator=(const MyClass& other)
{
if (this != &other)
{
data = other.data;
dataPtr.reset(other.dataPtr ? new UnmovableClass(*other.dataPtr)
: nullptr);
}
return *this;
}
MyClass(MyClass&&) = default;
MyClass& operator=(MyClass&&) = default;
friend void swap(MyClass& first, MyClass& second)
{
using std::swap;
swap(first.data, second.data);
swap(first.dataPtr, second.dataPtr);
}
};
The destructor could be implicitly defaulted above if desired. Everything else needs to be explicitly defined or defaulted for this example.
Reference: http://accu.org/content/conf2014/Howard_Hinnant_Accu_2014.pdf
The copy/swap idiom will likely cost you performance (see the slides). For example ever wonder why high performance / often used std::types like std::vector and std::string don't use copy/swap? Poor performance is the reason. If BigClass contains any std::vectors or std::strings (which seems likely), your best bet is to call their special members from your special members. The above is how to do that.
If you need strong exception safety on the assignment, see the slides for how to offer that in addition to performance (search for "strong_assign").
It's been a long time since I asked this question, and I've known the answer for a while now, but I've put off writing the answer for it. Here it is.
The answer is no. The Copy-and-swap idiom should not become the Copy-and-move idiom.
An important part of Copy-and-swap (which is also Move-construct-and-swap) is a way to implement assignment operators with safe cleanup. The old data is swapped into a copy-constructed or move-constructed temporary. When the operation is done, the temporary is deleted, and its destructor is called.
The swap behaviour is there to be able to reuse the destructor, so you don't have to write any cleanup code in your assignment operators.
If there's no cleanup behaviour to be done and only assignment, then you should be able to declare the assignment operators as default and copy-and-swap isn't needed.
The move constructor itself usually doesn't require any clean-up behaviour, since it's a new object. The general simple approach is to make the move constructor invoke the default constructor, and then swap all the members with the move-from object. The moved-from object will then be like a bland default-constructed object.
However, in this question's observer pattern example, that's actually an exception where you have to do extra cleanup work because references to the old object need to be changed. In general, I would recommend making your observers and observables, and other design constructs based around references, unmovable whenever possible.
In an already existing class of a project I am working on I encountered some strange piece of code: The assignment operator calls the copy constructor.
I added some code and now the assignment operator seems to cause trouble.
It is working fine though if I just use the assignment operator generated by the compiler instead. So I found a solution, but I'm still curious to find out the reason why this isn't working.
Since the original code is thousands of lines I created a simpler example for you to look at.
#include <iostream>
#include <vector>
class Example {
private:
int pValue;
public:
Example(int iValue=0)
{
pValue = iValue;
}
Example(const Example &eSource)
{
pValue = eSource.pValue;
}
Example operator= (const Example &eSource)
{
Example tmp(eSource);
return tmp;
}
int getValue()
{
return pValue;
}
};
int main ()
{
std::vector<Example> myvector;
for (int i=1; i<=8; i++) myvector.push_back(Example(i));
std::cout << "myvector contains:";
for (unsigned i=0; i<myvector.size(); ++i)
std::cout << ' ' << myvector[i].getValue();
std::cout << '\n';
myvector.erase (myvector.begin(),myvector.begin()+3);
std::cout << "myvector contains:";
for (unsigned i=0; i<myvector.size(); ++i)
std::cout << ' ' << myvector[i].getValue();
std::cout << '\n';
return 0;
}
The output is
myvector contains: 1 2 3 4 5
but it should be (an in fact is, if I just use the compiler-generated assignment operator)
myvector contains: 4 5 6 7 8
Your operator= does not do what everyone (including the standard library) thinks it should be doing. It doesn't modify *this at all - it just creates a new copy and returns it.
It's normal to re-use the copy constructor in the copy assignment operator using the copy-and-swap idiom:
Example& operator= (Example eSource)
{
swap(eSource);
return *this;
}
Notice how the operator takes its parameter by value. This means the copy-constructor will be called to construct the parameter, and you can then just swap with that copy, effectively assigning to *this.
Also note that it's expected from operator= to return by reference; when overloading operators, always follow the expected conventions. Even more, the standard actually requires the assignment operator of a CopyAssignable or MoveAssignable type to return a non-const reference (C++11 [moveassignable] and [copyassignable]); so to correctly use the class with the standard library, it has to comply.
Of course, it requires you to implement a swap() function in your class:
void swap(Example &other)
{
using std::swap;
swap(pValue, other.pValue);
}
The function should not raise exceptions (thanks #JamesKanze for mentioning this), no to compromise the exception safety of the operator=.
Also note that you should use the compiler-generated default constructors and assignment operators whenever you can; they can never get out of sync with the class's contents. In your case, there's no reason to provide the custom ones (but I assume the class is a simplified version for posting here).
The assignment operator you found is not correct. All it does is make a copy of eSource, but it's supposed to modify the object on which it is called.
The compiler-generated assignment operator for the class is equivalent to:
Example &operator= (const Example &eSource)
{
pValue = eSource.pValue;
return *this;
}
For this class there's no point implementing operator=, since the compiler-generated version basically cannot be improved on. But if you do implement it, that's the behaviour you want even if you write it differently.
[Alf will say return void, most C++ programmers will say return a reference. Regardless of what you return, the vital behaviour is an assignment to pValue of the value from eSource.pValue. Because that's what the copy assignment operator does: copy from the source to the destination.]
First of all, operator=() should return a reference:
Example& operator=(const Example& eSource)
{
pValue = eSource.pValue;
return *this;
}
Mind that your version returns a copy of tmp so in fact it performs two copies.
Second of all, in your class there's no need to even define custom assignment operator or copy constructor. The ones generated by compiler should be fine.
And third of all, you might be interested in copy-and-swap idiom: What is the copy-and-swap idiom?
Probably the most frequent correct implementation of operator=
will use the copy constructor; you don’t want to write the same
code twice. It will do something like:
Example& Example::operator=( Example const& other )
{
Example tmp( other );
swap( tmp );
return *this;
}
The key here is having a swap member function which swaps the
internal representation, while guaranteeing not to throw.
Just creating a temporary using the copy constructor is not
enough. And a correctly written assignment operator will always
return a reference, and will return *this.
I want to redirect my copy operator to my copy constructor. Where in the latter I implement the proper logic for copying/contructing a new class based on the old avaialble class.
However, how is the proper way to do this? I "think" this one is maybe leaking memory, but I don't know how to do it without passing a pointer:
MyClass& MyClass::operator=(const MyClass& a) {
MyClass* b = new MyClass(a);
return *b;
}
Is this OK? If is not, what would be the proper way? Should I change the body of the method or the prototype?
Thank you.
No, an operator= should set the current object attributes to be the same as the object assigned. Your method assigns a new object on the heap, returns it as a reference (essentially leaking it) and leaves the object the operator was called on completely unchanged.
You should implement a method called, for example, CopyFrom(), which assigns all the object's attributes to match those of the passed in object (deep copying any heap allocated pointers whose lifetime is managed by MyClass) and then call THAT from both your copy constructor and your operator=.
class MyClass
{
public:
MyClass( const MyClass& in )
{
CopyFrom( in );
}
MyClass& operator=( const MyClass& in )
{
CopyFrom( in );
return *this;
}
private:
void CopyFrom( const MyClass& in )
{
... copies in's attributes into self.
}
};
As a rule, a copy assignment operator should never create a copy. Rather, it should copy data into the existing object that it's called on (the left-hand side of the assignment). For example:
class MyClass
{
public:
MyClass & operator = (const MyClass & RHS)
{
// Copy data from RHS into 'this'
m_value = RHS.m_value;
return *this;
}
private:
int m_value;
};
In this case, defining your own copy constructor isn't necessary because the default (compiler-provided) one would work fine. It's just an illustration though.
Unfortunately you can't invoke the copy constructor on the existing object. The copy-swap pattern is an alternative, but it can be less efficient.
Unless you're storing pointers inside of MyClass, the correct copy assignment operator is the default-generated one. If, however you need to implement one, you can write it in terms of your copy-constructor via the copy-swap idiom:
MyClass& MyClass::operator = (MyClass const& a) {
MyClass temp(a); // Call the copy constructor
using std::swap;
swap(temp, *this);
return *this;
}
The reason for the using std::swap is to enable argument-dependent lookup. If you define your own swap function for MyClass, it will be called. Else std::swap will be used as a fallback. (EDIT: You do in fact need to implement a custom swap in this case, or else you will get infinite recursion. std::swap will use the assignment operator, which will call std::swap, which will call the...)
The reason that this idiom is well-liked is because std::swap is a no-throw function. If your copy-constructor were to throw an exception, then your object you're assigning to is still in a valid state.
The "proper way" is to implement the assignment operator like an assignment operator: modify the contents of the object on which the operator is being called and return a reference to it.
Your current implementation will result in a memory leak, AND doesn't do any assignment (which is the main point of the assignment operator).
If you only want to write the assignment code once, and your class doesn't allocate memory in the constructor, you could do this:
MyClass::MyClass(const MyClass& a) {
*this = a;
}
MyClass& MyClass::operator=(const MyClass& a) {
if (&a == this)
return *this;
// Do assignment
return *this;
}
But I wouldn't recommend it.
Your code is totally wrong (sorry)! The assignment operator does
not assign anything, but allocates a pointer to a MyClass object,
creating a memory leak. My advice: avoid pointers or use some
smart pointer (shared_ptr, unique_ptr), but that is just a side note.
Maybe this is helpful:
#include <iostream>
#include <limits>
class X
{
public:
X(std::size_t n)
: m_size(n), m_data(new int[n])
{
std::cout << "Construct" << std::endl;
}
~X()
{
std::cout << "Destruct" << std::endl;
delete [] m_data;
}
// Exception safe assignment.
// Note: I am passing by value to enable copy elision and
// move semantics.
X& operator = (X x) {
std::cout << "Assign" << std::endl;
x.swap(*this);
return *this;
}
void swap(X& x) {
std::swap(m_size, x.m_size);
std::swap(m_data, x.m_data);
}
std::size_t size() const { return m_size; }
private:
std::size_t m_size;
int* m_data;
};
int main()
{
X x(1);
try {
x = X(2);
// To provoke an exception:
std::size_t n = std::numeric_limits<std::size_t>::max();
x = X(n);
}
catch(...) {
std::cout << "Exception" << std::endl;
}
std::cout << "Size: " << x.size() << std::endl;
return 0;
}
If you absolutely want to implement the assignment operator by copy constructor, use the following:
MyClass& MyClass::operator=(const MyClass& o)
{
this->~MyClass(); // destroy current object
new(this) MyClass(o); // use the copy constructor
return *this;
}
I cannot think of any situation in which this would be the best thing to do (other answers describe ways of implementation that are better in some situations).
Maybe (just trying to make things up here) if MyClass contains hundreds of int/float fields, and several dynamically-allocated pointers?
Duplicating them in constructor and assignment operator is too tedious and error-prone
Having a copying function that both constructor and assignment operator call - not ideal, because pointers have to be set to NULL first
The code above - will work with no additional effort!
However, having bare (non-smart) pointers in your class is discouraged. If you have such a class, then you have far worse problems than non-working assignment operator - you have to refactor first, and the problem will go away, together with all other bugs.
I want to have a copy of the currently running instance.
When i change a value in the copy, original object is also affected. The copy acts as an instance.
How to avoid this? I need to create an independent copy of the calling object.
Set operator+(Set s){
Set temp = *this;
for(int i=0; s.elements[i] != '\0'; i++){
temp(s.elements[i]);
}
temp.elements[0] = 'X'; // <- this affects calling object also :(
return temp;
}
The problem is that Set temp = *this; makes a shallow copy, not a deep copy. You will have to modify the copy constructor and assignment operators for the Set class so that they make copies of all the member/contained objects.
E.g:
class Set
{
public:
Set()
{
elements = new SomeOtherObject[12];
// Could make elements a std::vector<SomeOtherObject>, instead
}
Set(const Set& other)
{
AssignFrom(other);
}
Set& operator=(const Set& other)
{
AssignFrom(other);
return *this;
}
private:
void AssignFrom(const Set& other)
{
// Make copies of entire array here, as deep as you need to.
// You could simply do a top-level deep copy, if you control all the
// other objects, and make them do top-level deep copies, as well
}
SomeOtherObject* elements;
};
Not that your function already makes two copies, since it takes its argument and returns its result per copy:
Set operator+(Set s);
So you wouldn't have to copy s, because it's already copied. I suppose this is involuntarily, so you might want to read about how to pass objects to functions and how to return objects from function in C++.
The problem you're reporting, though, hints at your copy constructor not working properly. Did you implement the copy constructor or are you using the compiler-supplied one?
This probably depends on how Set is implemented. If the assignment operator and the copy constructor haven't been overloaded to do a deep copy(including elements) then it won't work as expected.
Have you implemented a copy constructor for your class?
Default copy constructor will copy any pointer in your class, but not the content you are pointing to. You need to create a copy constructor or overload the '=' operator.
I would avoid a char pointer completely and use std::string instead. This way you dont even need a copy constructor and an assigment operator because the compiler generated once will do just fine. (because 'elements' of the 'Set' class is copy-constructible and has an assignment operator)
Here is my solution:
#include <iostream>
#include <string>
class Set{
std::string elements;
public:
Set() {
elements = "";
}
explicit Set(char* _elements) {
if (_elements)
elements = _elements;
}
Set operator+(const Set& s){
Set temp(*this);
temp.elements += s.elements;
return temp;
}
};
Btw. I added a constructor from char* so that 'elements' can somehow be initialized from outside. Not sure if this is what you wanted.
Ok. I went through rule of three and did the following changes... Can you point out what's wrong with this?
#include<iostream>
#include<cstring>
using namespace std;
class Set{
char *elements;
public:
Set() {
elements = new char('\0');
index = -1;
}
Set(const Set& cpy){
*this = cpy;
}
Set operator+(Set s){
Set temp = *this; // IMPORTANT! copy constructor of Set is called, "this" is passed as argument
// * = current OBJECT, else returns ADDRESS of current object
for(int i=0; s.elements[i] != '\0'; i++){
temp(s.elements[i]);
}
return temp;
}
Set& operator=(Set s){
delete [] elements;
elements = new char[strlen(s.elements) + 1];
strcpy(elements, s.elements); //overrides element of "this"
return *this;
}
};