A way to implement assignment by 'new' - c++

There are three way to use keyword 'new'.
First is the normal way. Suppose Student is a class.
Student *pStu=new Student("Name",age);
Second way . Only ask for the memory space without calling the constructor.
Student *pArea=(Student*)operator new(sizeof(student));//
Third way is called 'placement new'. Only call the constructor to initialize the meomory space.
new (pArea)Student("Name",age);
So, I wrote some code below.
class Student
{
private:
std::string _name;
int _age;
public:
Student(std::string name, int age):_name(name), _age(age)
{
std::cout<<"in constructor!"<<std::endl;
}
~Student()
{
std::cout<<"in destructor!"<<std::endl;
}
Student & assign(const Student &stu)
{
if(this!=&stu)
{
//here! Is it a good way to implement the assignment?
this->~Student();
new (this)Student(stu._name,stu._age);
}
return *this;
}
};
This code is ok for gcc. But I'm not sure if it would cause errors or it was dangerous to call destructor explicitly. Call you give me some suggestions?

The problem with a "replacement-assignment" is that it's not exception safe. Consider this simplified, generic approach:
struct Foo
{
Foo & operator=(Foo const & rhs)
{
if (this == &rhs) { return *this; }
~Foo();
::new (this) Foo(rhs); // may throw!
}
// ...
};
Now if the copy constructor throws an exception, you're in trouble. You've already called your own destructor, so the inevitable next destructor call will cause undefined behaviour. You also cannot change the order of operations around, since you don't have any other memory.
I actually asked about this sort of "stepping on a landmine" behaviour in a question of mine.

But I'm not sure if it would cause errors or it was dangerous to call destructor explicitly. Call you give me some suggestions?
The code as you wrote it has three major drawbacks:
it is difficult to read
it is optimized for the uncommon case (always performs the self assignment check although you rarely perform self assignment in practice)
it is not exception safe
Consider using the copy and swap idiom:
Student & assign(Student stu) // pass stu by value, constructing temp instance
{ // this is the "copy" part of the idiom
using namespace std;
swap(*this, stu); // pass current values to temp instance to be destroyed
// and temp values to *this
return *this;
} // temp goes out of scope thereby destroying previous value of *this
This approach is exception-safe if swap doesn't throw and it has two potential drawbacks:
if Student is part of a class hierarchy (has virtual members) creating a temporary instance will be more costly.
in the case of self assignment the implementation should be a no-op (or a self equality test), but in that case it will create an extra object instance. The self-assignment case is very rare.

Your suggestion is a major anti-pattern: if the new terminates
with an exception, you'll get undefined behavior, and if someone
tries to derive from your class, all sorts of weird things may
occur:
DerivedStudent a;
DerivedStudent b;
a = b; // Destructs a, and reconstructs it as a Student
and when a goes out of scope, it is the destructor of
DerivedStudent which will be called.
As a general rule, if you have to test for self-assignment, your
assignment operator is not exception safe.
The goal of this idiom, of course, is to avoid code duplication
between the copy constructor and the assignment operator, and to
ensure that they have the same semantics. Generally, the best
way to do this is to use the swap idiom, or something similar:
Student&
Student::operator=( Student const& other )
{
Student tmp( other );
swap( tmp );
return *this;
}
void
Student::swap( Student& other )
{
myName.swap( other.myName );
std::swap( myAge, other.myAge );
}
(One final, unrelated point. In practice, you're going to run
into conflicts with names which begin with an underscore. In
general, it's best to avoid leading or trailing underscore.)

Related

C++ move constructor for an owning pointer member

I'm asking something that looks trivial but I had problems with. Let's assume, for the sake of explanation, a structure like this one:
class MyClass{
int* m_Number;
public:
int value() const {return *m_Number;}
void setValue(int val){*m_Number=val;}
MyClass() : m_Number(new int(3)){}
~MyClass() {if(m_Number) delete m_Number;}
MyClass(const MyClass& other):m_Number(new int(*other.m_Number)){}
MyClass& operator=(const MyClass& other){if(m_Number) *m_Number=*other.m_Number; else m_Number=new int(*other.m_Number); return *this;}
MyClass& operator=(MyClass&& other){std::swap(m_Number,other.m_Number); return *this;}
MyClass(MyClass&& other){
//????
}
}
What should I put in there?
My options are:
1)
MyClass(MyClass&& other)
:m_Number(other.m_Number)
{
other.m_Number=nullptr;
}
But then the moved from object is not in a valid state. calling value() should return something valid but undetermined while here I just segfault. I could check m_Number in value() and setValue() but you realise it's a huge drag in real code.
2)
MyClass(MyClass&& other)
:m_Number(other.m_Number)
{
other.m_Number= new int(3);
}
But a move constructor that can throw is a no go (or at least as I understand it) and it's also a drag of the performance enhancement, in fact this code is equal or worse that the copy constructor.
What do you think?
Did I miss something?
Is there a preferred way to go?
Thanks in advance
Edit: This question received an answer from the convener of the std committee and it fundamentally disagrees with the answers to this post. You can find it in this article https://herbsutter.com/2020/02/17/move-simply/
Firstly, there's no reason to use new and delete here, you should be using make_unique<int> to create the object and a unique_ptr<int> to manage it automatically. But that doesn't solve your problem, of what the move constructor can do. There are some other options in addition to the two you suggest:
3) don't give it a move constructor, just leave it as copyable
4) document that calling value or setValue on a moved-from object is not allowed, and leave the moved-from object with a null pointer. Depending where moves happen in your program that might be fine. If moved-from objects are never accessed, everything just works.
4a) As above, but add sanity checks in case it does ever happen:
int value() const {
assert(m_Number != nullptr);
return *m_Number;
}
or:
int value() const {
if (m_Number == nullptr)
throw std::logic_error("accessed a moved-from object");
return *m_Number;
}
5) Add checks to setValue to re-initialize the object with a new int if it's currently null, and make value return some default value:
int value() const { return m_Number ? *m_Number : 0; }
void setValue(int val) {
if (!m_Number)
m_Number = new int(val);
else
*m_Number = val;
}
You are dereferencing your pointer in the .value() call. You will always segfault in the event that m_Number is invalid.
Your first solution to the move constructor is correct, you should set the 'other' object to a default state. To solve this, you could make your .value() method throw, or return a default value in the event of non-existent resource.
Your destructor already accounts for the null case, so make sure the rest of it accounts for it as well.
Your first approach is probably the best if modifying MyClass such that m_Number = nullptr is a valid state is reasonable to do (and would be best practice if it were the default state too). I'd argue if having no-heap memory associated with MyClass is not a valid state, you should be allocating it inside of a std::unique_ptr, and passing around a raw pointer to that instead of implementing a move constructor.
If 1. is not an option, this is a reasonable way to go. While there certainly is a performance hit for allocating un-needed memory, it is quite minor, especially considering you are constructing a class as part of this operation (which itself requires memory). If its a small chunk of memory that is needed as in your example, the memory allocator will likely draw it from its own pool without the need of a system call. If it is a large chunk of memory (as I would expect since you're implementing move semantics), then on most modern operating systems it will be a lazy allocation (so it would still be better than a copy constructor).
Do not use raw owning pointers, convert your class to use std::unique_ptr, and all your problems will go away.

What are problems with writing assignment like this?

I had a conversation with a friend of mine about object assignment and construction the other day, and he made a point that assignment a = b for objects is (semantically) equivalent to destroying a and then re-constructing it from b (at the same place).
But of course, nobody (I think) writes assignment operators like this:
class A {
A& operator=(const A& rhs) {
this->~A();
this->A(rhs);
return *this;
}
A& operator=(A&& rhs) {
this->~A();
this->A(std::move(rhs));
return *this;
}
// etc.
};
[Notice: I have no clue how to manually call constructors/destructors on existing objects (I never had to do that!), so their invocations may make no formal sense, but I guess you can see the idea.]
What are the problems with this approach? I imagine there has to be a main show-stopper, but the bigger the list, the better.
There is a misused contruction, here:
class A {
A& operator=(const A& rhs) {
if(&a==this) return *this;
this->~A();
new(this) A(rhs);
return *this;
}
A& operator=(A&& rhs) {
if(&a==this) return *this;
this->~A();
new(this) A(std::move(rhs));
return *this;
}
// etc.
};
This is correct respect to the inplace ctor/dtor semantics, and thsi is what std::allocator does to destroy and construct elements in a buffer, so that must be correct, right?
Well... not properly: it all is about what A in fact contains and what the A ctor actually does.
If A just contains basic types and does not own resources that's fine, it works. It's just not idiomatic, but correct.
If A contains some other resources, that need to be acquired, managed and released well... you may be in trouble. And you also are if A is polymorphic (if ~A is virtual you destroy the entire object, but then you reconstruct just the A subobject).
The problem is that a constructor that acquires resources may fail, and an object that fails in construction and throws must not be destroyed since it has been never "constructed".
But if you are "assigning", you are not "creating", and if the in-place ctor fails, your object will exist (because it pre-exist in its own scope), but is in a state that cannot be managed by a further destruction: think to
{
A a,b;
a = b;
}
At the } b and a will be destroyed but if A(const A&) failed in a=b, and a throw is made in A::A, a is not existing, but will be improperly destroyed at the } that throw will immediately jump to.
A more idiomatic way is to have
class A
{
void swap(A& s) noexcept
{ /* exchanging resources between existing objects should never fail: you just swap pointers */ }
public:
A() noexcept { /* creates an object in a "null" recognizable state */ }
A(const A& s) { /* creates a copy: may fail! */ }
A(A&& s) noexcept { /*make it as null and... */ swap(s); } // if `s` is temporary will caryy old resource deletionon, and we keep it's own resource going
A& operator=(A s) noexcept { swap(s); return *this; }
~A() { /* handle resource deletion, if any */ }
};
Now,
a=b
will create a b copy as the s parameter in operator= (by means of A::A(const A&)).
If this fails, s will not exist and a and b are still valid (with their own old values), hence at scope exiting will be destroyed as normally.
If the copy succeed, the copyed resources and the actual a's will be exchanged, and when s dies at the } the old-a resources will be freed.
By converse
a = std::move(b)
Will make b as-temporary, the s parameter constructed via A(A&&), so b will swap with s (and becomes null) than s will swap with a. At the end, s will destroy old a resources, a will receive old b's and b will be in null state (so it can die peacefully when its scope ends)
The problem of "making A as null" must be implemented in both A() and A(A&&).
This may be by means of an helper member (an init, just like a swap) or by specifying member initializers, or by defining default initialization values for members (once for all)
First of all, calling the destructor manually is required only if the object was constructed using an overloaded operator new() with some expections like using the std::nothrow overloads.
And what you got to understand is the difference between copy construction and assignment operator: copy constructor is called when a new object is created from an existing object, as a copy of the existing object. And assignment operator is called when an already initialized object is assigned a new value from another existing object.
To sum up, example of assignment operator you've provided doesn't make sense - it got to have different semantics.
If you have further questions, leave a comment.
First it is not legal to call a copy constructor directly (at least in C++ compliant compilers.. VS2012 allows that) so the following isn't allowed:
// assignment operator
A& operator=(const A& rhs) {
this->~A();
this->A::A(rhs); <--- Invalid use
at that point you can either rely on compiler optimizations (see copy elision and RVO) or allocate it on the heap.
Many issues can arise if you try to do the above:
1) You might have exceptions thrown in the expression for the copy constructor
In this case you will have
// assignment operator
A& operator=(const A& rhs) {
cout << "copy assignment called" << endl;
this->~A();
A newObj(rhs); // Can throw and A is in invalid state!
return newObj;
}
To make it safe you should use the copy-and-swap idiom:
set& set::operator=(set const& source)
{
/* You actually don't need this. But if creating a copy is expensive then feel free */
if (&source == this)
return;
/*
* This line is invoking the copy constructor.
* You are copying 'source' into a temporary object not the current one.
* But the use of the swap() immediately after the copy makes it logically
* equivalent.
*/
set tmp(source);
this->swap(tmp);
return *this;
}
void swap(set& dst) throw ()
{
// swap member of this with members of dst
}
2) You might have problems with dynamically allocated memory
In case two instances of A shared a pointer, you might have a dangling pointer before being able to release it
a = a; // easiest case
...
// assignment operator
A& operator=(const A& rhs) {
this->~A(); <-- Freeing dynamically allocated memory
this->A::A(rhs); <--- Getting a pointer to nowhere
3) As Emilio noted, if the class is polymorphic you're not going to be able to re-instantiate that subclass (unless you trick it somehow with a CRTP-like technique)
4) Finally assignment and copy construction are two different operations. If A contains resources which are expensive to re-acquire, you might find yourself in a lot of troubles.

Pros and Cons of usage of reference in case of PIMPL idiom

As mentioned here you can use reference (d-reference) instead of pointer (d-pointer) in case of PIMPL idiom.
I'm trying to understand if there are any serious issues with this implementation and what are the pros and cons.
Pros:
Shorter syntax because of usage of "." instead of "->".
...
Cons:
What if the new ObjectPivate() fails and new doesn't throw (e.g.: new(std::nothrow) or custom new) and returns nullptr instead? You need to implement additional stuff to check if the referance is valid. In case of pointer you just use:
if (m_Private)
m_Private->Foo();
In rare case of multiple constructors for the Object with complex initialisation logic the solution could be not applicable. [© JamesKanze]
It fills more natural to use pointer for memory management. [© JamesKanze]
Some additional implementation details needs to be considered (use of swap()) to ensure the exception-safety (e.g. implementation of assignment operator) [© Matt Yang]
...
Here the sample code for illustration:
// Header file
class ObjectPrivate;
class Object
{
public:
Object();
virtual ~Object();
virtual void Foo();
private:
ObjectPrivate& m_Private;
};
// Cpp file
class ObjectPrivate
{
public:
void Boo() { std::cout << "boo" << std::endl; }
};
Object::Object() :
m_Private(* new ObjectPrivate())
{
}
Object::~Object()
{
delete &m_Private;
}
void Object::Foo()
{
m_Private.Boo();
}
It's really just a matter of style. I tend to not use
references in classes to begin with, so using a pointer in the
compilation firewall just seems more natural. But there's
usually no real advantage one way or the other: the new can
only fail by means of an exception.
The one case where you might favor the pointer is when the
object has a lot of different constructors, some of which need
preliminary calculations before calling the new. In this
case, you can initialize the pointer with NULL, and then call
a common initialization routine. I think such cases are rare,
however. (I've encountered it once, that I can recall.)
EDIT:
Just another style consideration: a lot of people don't like something like delete &something;, which is needed if you use references rather than pointers. Again, it just seems more natural (to me, at least), that objects managing memory use pointers.
It's not convenient to write exception-safe code I think.
The first version of Object::operator=(Object const&) might be:
Object& operator=(Object const& other)
{
ObjectPrivate *p = &m_Private;
m_Private = other.m_Private; // Dangerous sometimes
delete *p;
}
It's dangerous if ObjectPrivate::operator=(ObjectPrivate const&) throws exception. Then what about using a temporary variable? Aha, no way. operator=() has to be invoked if you want change m_Private.
So, void ObjectPrivate::swap(ObjectPrivate&) noexcept can act as our savior.
Object& operator=(Object const& other)
{
ObjectPrivate *tmp = new ObjectPrivate(other.m_Private);
m_Private.swap(*tmp); // Well, no exception.
delete tmp;
}
Then consider the implementation of void ObjectPrivate::swap(ObjectPrivate&) noexcept. Let's assume that ObjectPrivate might contain a class instance without swap() noexcept or operator=() noexcept. I think it's hard.
Alright then, this assumption is too strict and not correct sometimes. Even so, it's not necessary for ObjectPrivate to provide swap() noexcept in most cases, because it's usually a helper structure to centralize data.
By contrast, pointer can save a lot of brain cells.
Object& operator=(Object const& other)
{
ObjectPrivate *tmp = new ObjectPrivate(*other.p_Private);
delete p_Private;
p_Private = tmp; // noexcept ensured
}
It's much more elegant if smart pointers are used.
Object& operator=(Object const& other)
{
p_Private.reset(new ObjectPrivate(*other.p_Private));
}
Some quick and obvious additions:
Pro
The reference must not be 0.
The reference may not be assigned another instance.
Class responsibilities/implementation are simpler due to fewer variables.
The compiler could make some optimizations.
Con
The reference may not be assigned another instance.
The reference will be too restrictive for some cases.

Questions about a Segmentation Fault in C++ most likely caused by a custom copy constructor

I'm getting a segmentation fault which I believe is caused by the copy constructor. However, I can't find an example like this one anywhere online. I've read about shallow copy and deep copy but I'm not sure which category this copy would fall under. Anyone know?
MyObject::MyObject{
lots of things including const and structs, but no pointers
}
MyObject::MyObject( const MyObject& oCopy){
*this = oCopy;//is this deep or shallow?
}
const MyObject& MyObject::operator=(const MyObject& oRhs){
if( this != oRhs ){
members = oRhs.members;
.....//there is a lot of members
}
return *this;
}
MyObject::~MyObject(){
//there is nothing here
}
Code:
const MyObject * mpoOriginal;//this gets initialized in the constructor
int Main(){
mpoOriginal = new MyObject();
return DoSomething();
}
bool DoSomething(){
MyObject *poCopied = new MyObject(*mpoOriginal);//the copy
//lots of stuff going on
delete poCopied;//this causes the crash - can't step into using GDB
return true;
}
EDIT: Added operator= and constructor
SOLVED: Barking up the wrong tree, it ended up being a function calling delete twice on the same object
It is generally a bad idea to use the assignment operator like this in the copy constructor. This will default-construct all the members and then assign over them. It is much better to either just rely on the implicitly-generated copy constructor, or use the member initializer list to copy those members that need copying, and apply the appropriate initialization to the others.
Without details of the class members, it is hard to judge what is causing your segfault.
According to your code you're not creating the original object... you're just creating a pointer like this:
const MyObject * mpoOriginal;
So the copy is using bad data into the created new object...
Wow....
MyObject::MyObject( const MyObject& oCopy)
{
*this = oCopy;//is this deep or shallow?
}
It is neither. It is a call to the assignment operator.
Since you have not finished the construction of the object this is probably ill-advised (though perfectly valid). It is more traditional to define the assignment operator in terms of the copy constructor though (see copy and swap idium).
const MyObject& MyObject::operator=(const MyObject& oRhs)
{
if( this != oRhs ){
members = oRhs.members;
.....//there is a lot of members
}
return *this;
}
Basically fine, though normally the result of assignment is not cont.
But if you do it this way you need to divide up your processing a bit to make it exception safe. It should look more like this:
const MyObject& MyObject::operator=(const MyObject& oRhs)
{
if( this == oRhs )
{
return *this;
}
// Stage 1:
// Copy all members of oRhs that can throw during copy into temporaries.
// That way if they do throw you have not destroyed this obbject.
// Stage 2:
// Copy anything that can **not** throw from oRhs into this object
// Use swap on the temporaries to copy them into the object in an exception sage mannor.
// Stage 3:
// Free any resources.
return *this;
}
Of course there is a simpler way of doing this using copy and swap idum:
MyObject& MyObject::operator=(MyObject oRhs) // use pass by value to get copy
{
this.swap(oRhs);
return *this;
}
void MyObject::swap(MyObject& oRhs) throws()
{
// Call swap on each member.
return *this;
}
If there is nothing to do in the destructor don't declare it (unless it needs to be virtual).
MyObject::~MyObject(){
//there is nothing here
}
Here you are declaring a pointer (not an object) so the constructor is not called (as pointers don;t have constructors).
const MyObject * mpoOriginal;//this gets initialized in the constructor
Here you are calling new to create the object.
Are you sure you want to do this? A dynamically allocated object must be destroyed; ostensibly via delete, but more usually in C++ you wrap pointers inside a smart pointer to make sure the owner correctly and automatically destroys the object.
int main()
{ //^^^^ Note main() has a lower case m
mpoOriginal = new MyObject();
return DoSomething();
}
But since you probably don't want a dynamic object. What you want is automatic object that is destroyed when it goes out of scope. Also you probably should not be using a global variable (pass it as a parameter otherwise your code is working using the side affects that are associated with global state).
int main()
{
const MyObject mpoOriginal;
return DoSomething(mpoOriginal);
}
You do not need to call new to make a copy just create an object (passing the object you want to copy).
bool DoSomething(MyObject const& data)
{
MyObject poCopied (data); //the copy
//lots of stuff going on
// No need to delete.
// delete poCopied;//this causes the crash - can't step into using GDB
// When it goes out of scope it is auto destroyed (as it is automatic).
return true;
}
What you are doing is making your copy constructor use the assignment operator (which you don't seem to have defined). Frankly I'm surprised it compiles, but because you haven't shown all your code maybe it does.
Write you copy constructor in the normal way, and then see if you still get the same problem. If it's true what you say about 'lots of things ... but I don't see any pointers' then you should not be writing a copy constructor at all. Try just deleting it.
I don't have a direct answer as for what exactly causes the segfault, but conventional wisdom here is to follow the rule of three, i.e. when you find yourself needing any of copy constructor, assignment operator, or a destructor, you better implement all three of them (c++0x adds move semantics, which makes it "rule of four"?).
Then, it's usually the other way around - the copy assignment operator is implemented in terms of copy constructor - copy and swap idiom.
MyObject::MyObject{
lots of things including const and structs, but no pointers
}
The difference between a shallow copy and a deep copy is only meaningful if there is a pointer to dynamic memory. If any of those member structs isn't doing a deep copy of it's pointer, then you'll have to work around that (how depends on the struct). However, if all members either don't contain pointers, or correctly do deep copies of their pointers, then the copy constructor/assignment is not the source of your problems.
It's either, depending on what your operator= does. That's where the magic happens; the copy constructor is merely invoking it.
If you didn't define an operator= yourself, then the compiler synthesised one for you, and it is performing a shallow copy.

Check for "self-assignment" in copy constructor?

Today in university I was recommended by a professor that I'd check for (this != &copy) in the copy constructor, similarly to how you should do it when overloading operator=. However I questioned that because I can't think of any situation where this would ever be equal to the argument when constructing an object.
He admitted that I made a good point. So, my question is, does it make sense to perform this checking, or is it impossible that this would screw up?
Edit: I guess I was right, but I'll just leave this open for a while. Maybe someone's coming up with some crazy cryptic c++ magic.
Edit2: Test a(a) compiles on MinGW, but not MSVS10. Test a = a compiles on both, so I assume gcc will behave somewhat similar. Unfortunately, VS does not show a debug message with "Variable a used without being initialized". It does however properly show this message for int i = i. Could this actually be considered a c++ language flaw?
class Test
{
Test(const Test &copy)
{
if (this != &copy) // <-- this line: yay or nay?
{
}
}
Test &operator=(const Test &rhd)
{
if (this != &rhd) // <-- in this case, it makes sense
{
}
}
};
Personally, I think your professor is wrong and here's why.
Sure, the code will compile. And sure, the code is broken. But that's as far as your Prof has gone with his reasoning, and he then concludes "oh well we should see if we're self-assigning and if we are, just return."
But that is bad, for the same reason why having a global catch-all catch(...) which does nothing is Evil. You're preventing an immediate problem, but the problem still exists. The code is invalid. You shouldn't be calling a constructor with a pointer to self. The solution isn't to ignore the problem and carry on. The solution is to fix the code. The best thing that could happen is your code will crash immediately. The worst thing is that the code will continue in an invalid state for some period of time and then either crash later (when the call stack will do you no good), or generate invalid output.
No, your professor is wrong. Do the assignment without checking for self-assignment. Find the defect in a code review or let the code crash and find it in a debug session. But don't just carry on as if nothing has happened.
This is valid C++ and calls the copy constructor:
Test a = a;
But it makes no sense, because a is used before it's initialized.
If you want to be paranoid, then:
class Test
{
Test(const Test &copy)
{
assert(this != &copy);
// ...
}
};
You never want to continue if this == &copy. I've never bothered with this check. The error doesn't seem to frequently occur in the code I work with. However if your experience is different then the assert may well be worth it.
Your instructor is probably trying to avoid this situtation -
#include <iostream>
class foo
{
public:
foo( const foo& temp )
{
if( this != &temp )
std::cout << "Copy constructor \n";
}
};
int main()
{
foo obj(obj); // This is any how meaning less because to construct
// "obj", the statement is passing "obj" itself as the argument
}
Since the name ( i.e., obj ) is visible at the time declaration, the code compiles and is valid.
Your instructor may be thinking of the check for self-assignment in the copy assignment operator.
Checking for self-assignment in the assignment operator is recommended, in both Sutter and Alexandrescu's "C++ Coding Standards," and Scott Meyer's earlier "Effective C++."
In normal situations, it seems like here is no need to. But consider the following situation:
class A{
char *name ;
public:
A & operator=(const A & rhs);
};
A & A::operator=(const A &rhs){
name = (char *) malloc(strlen(rhs.name)+1);
if(name)
strcpy(name,rhs.name);
return *this;
}
Obviously the code above has an issue in the case when we are doing self assignment. Before we can copy the content, the pointer to the original data will be lost since they both refer to same pointer. And that is why we need to check for self assignment. Function should be like
A & A::operator=(const A &rhs){
if(this != &rhs){
name = (char *) malloc(strlen(rhs.name)+1);
if(name)
strcpy(name,rhs.name);
}
return *this;
}
When writing assignment operators and copy constructors, always do this:
struct MyClass
{
MyClass(const MyClass& x)
{
// Implement copy constructor. Don't check for
// self assignment, since &x is never equal to *this.
}
void swap(MyClass& x) throw()
{
// Implement lightweight swap. Swap pointers, not contents.
}
MyClass& operator=(MyClass x)
{
x.swap(*this); return *this;
}
};
When passing x by value to the assignment operator, a copy is made. Then you swap it with *this, and let x's destructor be called at return, with the old value of *this. Simple, elegant, exception safe, no code duplication, and no need for self assignment testing.
If you don't know yet about exceptions, you may want to remember this idiom when learning exception safety (and ignore the throw() specifier for swap for now).
I agree that self check doesn't make any sense in copy constructor since object isn't yet created but your professor is right about adding the check just to avoid any further issue. I tried with/without self check and got unexpected result when no self check and runtime error if self check exists.
class Test
{
**public:**
Test(const Test& obj )
{
size = obj.size;
a = new int[size];
}
~Test()
{....}
void display()
{
cout<<"Constructor is valid"<<endl;
}
**private:**
}
When created copy constructor and called member function i didn
Test t2(t2);
t2.display();
Output:
Inside default constructor
Inside parameterized constructor
Inside copy constructor
Constructor is valid
This may be correct syntactically but doesn't look right.
With self check I got runtime error pointing the error in code so to avoid such situation.
Test(const Test& obj )
{
if(this != &obj )
{
size = obj.size;
a = new int[size];
}
}
Runtime Error:
Error in `/home/bot/1eb372c9a09bb3f6c19a27e8de801811': munmap_chunk(): invalid pointer: 0x0000000000400dc0
Generally, the operator= and copy constructor calls a copy function, so it is possible that self-assignment occurs.
So,
Test a;
a = a;
For example,
const Test& copy(const Test& that) {
if (this == &that) {
return *this
}
//otherwise construct new object and copy over
}
Test(const &that) {
copy(that);
}
Test& operator=(const Test& that) {
if (this != &that) { //not checking self
this->~Test();
}
copy(that);
}
Above, when a = a is executed, the operator overload is called, which calls the copy function, which then detects the self assignment.
Writing copy-assignment operators that are safe for self-assignment is in the C++ core guidelines and for a good reason. Running into a self-assignment situation by accident is much easier than some of the sarcastic comments here suggest, e.g. when iterating over STL containers without giving it much thought:
std::vector<Test> tests;
tests.push_back(Test());
tests.resize(10);
for(int i = 0; i < 10; i++)
{
tests[i] = tests[0]; // self when i==0
}
Does this code make sense? Not really.
Can it be easily written through carelessness or in a slightly more complex situation? For sure. Is it wrong? Not really, but even if so... Should the punishment be a segfault and a program crash? Heck no.
To build robust classes that do not segfault for stupid reasons, you must test for self-assignment whenever you don't use the copy-and-swap idiom or some other safe alternative. One should probably opt for copy-and-swap anyway but sometimes it makes sense not to, performance wise. It is also a good idea to know the self-assignment test pattern since it shows in tons of legacy code.