Today in university I was recommended by a professor that I'd check for (this != ©) 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 ©)
{
if (this != ©) // <-- 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 ©)
{
assert(this != ©);
// ...
}
};
You never want to continue if this == ©. 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.
Related
#include <iostream>
class Complex
{
double *arr;
int n;
public:
Complex() :n(0), arr(nullptr) {};
Complex(const Complex &a)
{
if (this != a)
{
this->~Complex();
copy(a);
}
}
void copy(const Complex &a)
{
n = a.n;
arr = new double[n];
std::copy(a.arr, a.arr + n, arr);
}
~Complex()
{
delete[] arr;
n = 0;
}
};
int main()
{
getchar();
getchar();
}
This is the code i have, as you can see, all i did so far is that i created class Complex and i created default constructor and i wanted to create copy constructor, which shouldn't be complicated, but when i tried to compile this, compiler says "no operator '!=' matches these operands" , now, i know that this is a pointer to the ongoing object, and a is my argument that is sent to function by reference, so i am wondering, do i need to treat this argument inside of a function as a regular variable even though it is sent as a reference? Could that be the problem? Or is it something else? Any help appreciated!
The line
if (this != a)
is a syntactic error since type of this is a pointer while a is a reference to an object. A syntactially correct form would be:
if (this != &a)
However, that is totally unnecessary in a copy constructor. In the copy constructor, you are creating a new object from another object. this != &a will always be true.
Secondly, don't use
this->~Complex();
in the function. You have not yet constructed the object. What sense does it make to call the destructor on it? Also, once you have called the destructor, the object is dead. Using the object after that is cause for undefined behavior.
Simlify your function to:
Complex(const Complex &a)
{
copy(a);
}
A constructor's job is to initialize an object under creation. There is no pre-existing object there yet, so doing the check this != &a is pointless (I fixed it to compare addresses, as you probably meant). The only way that condition will ever be false is if someone writes this Machiavellian piece of code
Complex a(a);
It's technically allowed but any compiler worth its salt will flag it with a nice shiny warning that this line needs to be fixed.
Now, because there is no object there yet (it is being created), calling its destructor makes your program have undefined behavior. It means you can't predict what will happen according to the C++ specification, which puts you on very shaky ground.
A straight forward constructor will do the job just fine:
Complex(const Complex &a) : n(a.n), arr(new double[a.n])
{
std::copy(a.arr, a.arr + n, arr);
}
Protect against Murphy, not Machiavelli. Defensive programming is good to stop someone from accidentally braking your code. But you can't stop someone intent on it, so don't write overly complex checks to try.
No, in if (this != a) you have this that is a pointer and a that is a reference.
Do: if (this != &a)
Since my copy operator does exactly what I want the assignment operator, to call the copy operator. I've looked at the similar posts I tried creating a new temp object which I called the copy operator on and then returning the temp, but this doesn't work. So I tried sending back a reference but that doesn't work either.
SelfDestructingMessage& SelfDestructingMessage::operator=(
SelfDestructingMessage &source){
this(source);
return *this;
}
How can I return a reference, not a copy?
You are close. You need a swap so that your assignment operator looks like so:
struct my_object
{
my_object& operator=(my_object const& other)
{
my_object tmp(other);
swap(*this,other);
return *this;
}
};
You want your swap to be nothrow so that you have exception safety. Note that you can eliminate the name tmp, and maybe allow some significant optimizations, if you just accept the parameter by value.
This is known as the "copy-swap idiom". There are numerous sources but it is best explained in Exceptional C++ by Sutter. He describes it in detail, some variations, and WHY it makes everything easier. When you pair it with pimpl you can gain a huge number of advantages...with some very minor effects on performance in most cases--sometimes it really matters and you can't do a pimpl (pointer indirection can confuse the prefetcher and such).
I don't know WTF Michael Roy is about. I have indeed seen crap like *this = other in the copy constructor but every single dev that has done that also did a lot of other really stupid stuff that ended up giving headaches to way too many people. Just because something is done by some people doesn't mean you need to follow them off a cliff into nonsense.
You're doing it backwards. The easiest way to reuse object copy code is to call the = operator from the copy constructor.
struct A
{
A(const A& other) { *this = other; }
A& operator = (const A& other) { /*....*/ return *this; }
};
C++ default copy constructors and operators are sufficient in most cases, you should rely on them whenever possible.
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.
Here you can see copy assignment operator implementation with self assignment check:
String & operator=(const String & s)
{
if (this != &s)
{
String(s).swap(*this); //Copy-constructor and non-throwing swap
}
// Old resources are released with the destruction of the temporary above
return *this;
}
This is good for self assignment, but bad for performance:
as each time it check as if statement (I don't know how much will be it optimal, considering the branch prediction)
Also we lose here copy elision for rvalue arguments
So I still don't understand if I would implement std::vector's operator= how I would implement it.
Yes, this code is superflous. And it is true that it is causing extra unncessary branch. With proper swap and move semantics, following should be much more performant:
String& String::operator=(String s) { // note passing by value!
std::swap(s, *this); // expected to juggle couple of pointers, will do nothing for self-assingment
return *this;
}
Also note, it is more benefical to accept the argument by value.
as each time it check as if statement (I don't know how much will be it optimal, considering the branch prediction)
I think you've got yourself in a premature optimization circle here.
Check for self assignment -> self-assignment is unnecessary if you wrote the code properly -> why not write swap explicitly if you mean it? -> we're back to square one
Realistically, I would just implement Allocator and not worry about it.
Also we lose here copy elision for rvalue arguments
I don't think so.
#include <iostream>
#define loud(x) std::cout << x << "\n";
struct foo
{
foo() { loud("default") }
~foo() { loud("destruct") }
foo(const foo&) { loud("copy") }
foo(foo&&) { loud("move") }
foo & operator=(const foo & s)
{
if (this != &s)
{
loud("copy assign")
}
return *this;
}
};
int main()
{
foo f;
foo g;
g = f;
}
Outputs:
default
default
copy assign
destruct
destruct
This is with -fno-elide-constructors.
You claim the branch may be a problem, but the assembly output for -O2 shows me that GCC doesn't even emit code for the operator= and just outputs the "copy assign" string directly. Yes, I realized I have a simplified example, but it really is the wrong end of the pile to start from.
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.)