C++ delete/destructor safe call [duplicate] - c++

This question already has answers here:
What is The Rule of Three?
(8 answers)
Closed 7 years ago.
I have a "twisted" question...
Suppose to have a class like
class MyClass {
public:
MyClass();
~MyClass();
MyClass& operator=(const MyClass& obj);
private:
int* mem;
};
where basically MyClass inits somehow mem (with a new call), the ~MyClass() deallocates mem with the delete operator.
Suppose moreover the operator = is overloaded with the code
MyClass& operator=(const MyClass& obj) {
if(this != &obj) {
//allocate memory of "this" with the "new" operator and copy "obj" data;
}
return *this;
}
my question is basically the following... With the following sequence of statements
//statement 1 for short
MyClass my_obj = some_obj_of_MyClass;
i guess everything is fine since the operator = allocates memory and copies the data, but with the following
//statement 2 for short
MyClass obj; //allocate memory for "mem"
obj = some_obj_of_MyClass;
if think it is not correct the implementation i proposed since i don't delete memory allocated early. I could call the destructor inside the assignment operator block, but in that case probably the statement 1 wouldn't be safe.
So what it is a safe way to implement everything?
I guess the problem here can be to understand when the destructor is called or how to implement it properly.

Your code violates the rule of three: Whenever a class provides a custom
copy constructor
copy assignment operator
destructor
it should likely provide all three. Because, if any of these is omitted, it is possible to construct use cases where bad things happen. For example, contrary to intuition, the statement
MyClass my_obj = some_obj_of_MyClass;
does not call the assignment operator. It calls the copy constructor, which you have not provided. As such, the default copy constructor is used which simply copies the pointer. Once either my_obj or some_obj_of_MyClass is destructed, the other will have a pointer to the deleted memory region.
On the other hand, the statements
MyClass obj;
obj = some_obj_of_MyClass;
will first default construct obj, and then call the assignment operator on it. The assignment operator must be written in a way that it works correctly in every possible state of obj (i. e. it must not leak memory or double delete memory). That is, if obj.mem is allocated, the assignment operator must either reuse that memory or deallocate it (possibly replacing it with a new allocation). If obj.mem can be a null pointer, then the assignment operator must include special handling of this special case.
So, your choice is basically, whether you want to allow the mem pointer to become a null pointer, If you don't allow this, your default constructor must allocate a dummy memory region that can be deallocated by the assignment operator.
You can also work around the rule of three by deleting the unimplemented functions. Your class could be declared like this:
class MyClass {
public:
MyClass();
MyClass(const MyClass&) = delete;
MyClass& operator=(const MyClass& obj);
~MyClass();
private:
int* mem;
};
This would disallow the statement
MyClass my_obj = some_obj_of_MyClass;
forcing user code to use the more verbose
MyClass obj;
obj = some_obj_of_MyClass;
but which makes the implementation of MyClass a bit simpler.

Related

What happens behind the scenes when manually calling a constructor in C++?

This is an educational question, I am interested in what happens behind the scenes when I do:
SomeClass x(arg1, arg2, arg3); // An instance of SomeClass is constructed.
x = SomeClass(arg4, arg5, arg6); // Intent is to create a new instance.
SomeClass does not have operator= implemented.
Does the space allocated to x simply get overwritten as if it was newly allocated memory or what exactly happens? And when is it a good idea?
This can best be explained with the help of a small example:
Live on Coliru
struct A {
A(int a) { cout << "A::ctor\n"; } //ctor
A(const A& a) { cout << "A::copy\n"; } //copy ctor
A& operator=(const A& a) { cout << "A::operator=\n"; } //copy assign
};
int main()
{
A a(2); //calls constructor
a = A(10); //calls constructor first, then copy assignment
}
Output:
A::ctor
A::ctor
A::operator
The above is pretty self explanatory. For the first, only the constructor gets called. For the second, first the constructor is called and then copy assignment.
SomeClass does not have operator= implemented.
That doesn't matter because the compiler can generate one for you. If you explicitly delete it, then the above code will not compile. However, if you have a move constructor defined then that will be used:
(I highly recommend you read The rule of three/five/zero and understand it. It is among the top 5 things in C++ that you should know.)
A& operator=(const A& a) = delete; //copy assign deleted
A& operator=(A&& other) { cout << "move assigned\n"; } //move assign available
Now you maybe wondering what will happen if both copy and move assign are available. Lets see:
A a(2); //ctor
a = A(10); //ctor + move assign
A b(3); //ctor
b = a; // copy assign only
a = std::move(b); // move assign
For a = A(10) move assign is invoked because A(10) is an rvalue of the same type as what is on the left hand side of the =.
For the last case a = std::move(b);, we explicitly cast b to an rvalue (yes that's what std::move() does). Since it's an rvalue now, move assignment is invoked.
Does the space allocated to x simply get overwritten as if it was newly allocated memory or what exactly happens?
First the temporary is created: A(10). Space will of course be allocated for it.
It's result is then assigned to a, so previous values in a get overwritten
destructor for the temporary will be called
And when is it a good idea?
It is a good idea when you need it, it depends on your usecase. Generally I would recommend that don't copy assign unnecessarily.
Second line is call of constructor followed by call to assignment operator. Assigment default to shallow copy of non-static members into existing storage.
If you defined something that prevented compiler to create default operator=, i.e. you defined move constructor or move assignment, no assignment is possible unless you declared your own (why it is so surprising?) If default shallow copy is fine, you can write following declaration:
SomeClass& operator(const SomeClass&) = default;
= default provides mechanism to declare "default" behavior of special functions.
Now there is move assignment and in such case one would be preferred if it declared in given context. But it won't be declared by compiler if user provided destructor or copy\move constructor\assignment operator.
SomeClass& operator(SomeClass&&) = default;
Difference between two assignments exists only for class-types where "move" semantics may include transfer of ownership. For trivial types and primitive types it's a simple copy.
Compiler allowed to elide some actions including creation of storage for temporary object, so resulting code may actually write new values directly into x storage, provided that such elision won't change program behavior.

When to delete copy constructor and assignment operator? [duplicate]

This question already has answers here:
Why and when delete copy constructor and operator=
(2 answers)
Closed 3 years ago.
I am often seeing such (simplified) code:
class A {
public:
A(int a) :a{a} {}
A(const A&) = delete;
A& operator=(const A&) = delete;
private:
int a;
}
Then, throughout the program, the class A is instantiated only once.
What are the practical reaons to delete a copy constructor and operator= and at the same time not make the class Singleton?
If you have a class that have vars allocating dynamic memory and you don't want to make a suitable copy ctor and cpy assignment operator to allocate a new memory to avoide having two pointers pointing to the same thing, you can delete the whole cpy ctor and cpy assignment operator.
If you want to disallow passing the object by value, you may delete them too.
When to delete copy constructor and assignment operator?
Copy constructor (and assignment) should be defined when ever the implicitly generated one violates any class invariant.
It should be defined as deleted when it cannot be written in a way that wouldn't have undesirable or surprising behaviour.
Probably the simplest example is the class std::unique_ptr. As the name implies, it has unique ownership of a pointer value. Copying the internal pointer would violate the invariant of unique ownership. That's what the implicit copy constructor (and assignment) would do. But there is no reasonable alternative either. Maybe you could implement a copy constructor that dynamically allocates a new object, and copy-initialises it from the originally pointed object. But then this pointer would have different behaviour from normal pointers whose copy will not cause allocation or creation of new pointed objects, which may be surprising and undesirable. Furthermore, std::unique_ptr needs to also be usable with pointed types that themselves might not be copyable.

Why to use copy constructor C++

As i know, when we assign one object is another default copy constructor will be called.
class class1 obj1;
class class2 obj2;
obj1(obj2); //default copy constructor will be called by compiler
So, when should I write explicitly the copy constructor?
In your case the copy-assignment operator will be called, not the copy-constructor. To call the copy-constructor you would have do to e.g.
class1 obj1;
class1 obj2 = obj1; // Invokes the copy-constructor in obj2
A good idea when to write a copy-constructor (or a copy-assignment operator, or a destructor) you can see by reading about the rule of three. In short, if you have any of a destructor, copy-constructor or copy-assignment operator, then you should probably have all of them.
Also, while the compiler will auto-generate copy-constructor and copy-assignment operator for you if you do not provide your own, you have to remember that those auto-generated function will only do a shallow copy. If you have e.g. pointers to memory you allocate in the object, the auto-generated functions will only copy the actual pointer, and not what it points to. This means that after a copy you have two objects both pointing to the same memory. If you delete the pointer in the destructor, and one of the objects are destructed, the other object will still have its pointer, but it will now point to deleted memory.
You will get a default copy constructor when you don't write one, provided you don't add any of the three or five to your class : destructor, copy or move assignment or constructor.
Sometimes this does the right thing. For example, if you just need a shallow copy, or if the member's corresponding functions fo the right thing, for example smart pointers.
If an object exist prior to the assign, then it does not involve a construction but the assignment operator, signatures are:
T& operator=( T const & ); // from l-value ref
T& operator=( T && ); // from r-value ref, since c++11
A frequent strategy is to write the assignment operator as the idiom "copy and swap" :
T& operator=( T const & o ) {
T val( o ); // you need to write the copy ctor
swap(*this,o); // you need to write the swap
return *this;
}
T& operator=( T && o ) {
T val( std::move(o) ); // you need to write the move ctor
swap(*this,o); // you need to write the swap
return *this;
}
The c++11 version of that strategy
T& operator=( T o ) noexcept { // copy done before the operator that can be noexcept ( swap have to too)
swap(*this,o); // you need to write the swap
return *this;
}
In some cases you will find that the way your objects should be copied is not trivial.
If you consider the class :
class Car {
string BrandName;
int NumberOfPassenger;
}
Then it is clear that when you'll be copying two objects, you'll simply want to copy them member by member. There's nothing special to do here so the defaut copy constructor will work just fine.
But imagine that the class is instead :
class Car {
string BrandName;
int NumberOfPassenger;
Mechanics EngineeringStuff;
}
Here Mechanics is a reference type. What the copy constructor will do is simply copying the reference to the new object, so both cars - car1 and car2 - will share the same EngineeringStuff. But a more natural behaviour would be to allocate manually a new Mechanics object when performing the copy, so the cars don't share the same wheels, motors etc...
More generally, it's usually when you have to deal with reference types or certain kind of business logic that you will need to explicitly write your copy constructor.

Copy Constructor classes

Here is my piece of code, please help me understand how to get the copy constructor correct. My class has no copy constructor written for it
test(cosnt test&obj){
////////something
}
But yet when I attempt to make a copy such as test t2(t1); it appears to be copied correctly! Can you please explain to me the reason that the copy seems to work even without an explicit copy constructor?
#include <iostream>
using namespace std;
class test{
int *ptr;
public:
test(int t=0){
ptr=new int;
*ptr=t;
}
void increment(){
(*ptr)++;
}
void display(){
cout<<*ptr<<endl;
}
};
int main(){
test t1;
t1.display();
t1.increment();
t1.display();
test t2(t1);//copy constructor works fine!
t2.display();
t1.increment();
t1.display();
t2.display();
}
C++ is so amazing that when you don't define a copy constructor, move constructor, copy assignment and move assignment for a class, the compiler will have to define them for you (the Standard says so). Of course you can delete them via:
func() = delete;
So for example, if you want to delete the implicit copy constructor in your example you would declare:
test(const test&) = delete;
and as you can see your code won't no longer compile.
The behavior of the implicit copy constructor is the one you would expect: it will copy construct each member of the class to the the other object. In this case it will copy construct the pointer value (not the pointed value), effectively make the two object share the same pointer.
Now, your program is leaking memory right? You have called new but no delete. Let's say you want to clean your resources up by inserting:
delete ptr;
in the destructor (it's a simplified version, of course you would need to define at least a proper move assignment and move constructor). You know what will happen? A nice and beautiful runtime error, telling you that the pointer you are trying to free has not been allocated. The reason why that is, is that both your objects (t1 and t2) destructors a will be called and they will both delete the same pointer. The first correctly and the second erroneously.
For this reason a Rule of three (now Rule of Five) has been established in the C++ community. But you know what? There's even a better rule which is called Rule of Zero. To sums it up (but you should really read about it) it says: don't do RAII yourself. I'd suggest you to follow the latter.
Now, let's discuss a little a bit of new. I'm sure you know this, but I'm here to prevent future damages: you don't need to use new at all in C++. Not anymore, in most cases.
Most of the things pointers once did (optional parameter, pass array by pointer, copyable reference, etc..) has now been "deprecated" (not in the literal sense: they are still there) in favor of sexier approaches (namely boost:optional/std::optional, std::array/std::vector, std::reference_wrapper). And even when all those fails to give you what you need, you can still use std::shared_ptr and std::unique_ptr.
So, please, don't use naked new pointers. Thanks.
If you did not define the copy constructor yourself then the compiler implicitly defines it instead of you. This copy constructor makes member-wise copies of class data members.
Relative to your example of code the implicitly defined copy constructor will copy data member ptr. As the result two or more objects can refer to the same memory.
Your class also needs destructor and the copy assignment operator.
These three specail functions can look the following way for your class
test( const test &rhs ) : ptr( new int( *rhs.ptr ) )
{
}
test & operator =( const test &rhs )
{
if ( this != &rhs )
{
int tmp = new int( *rhs.ptr );
delete ptr;
ptr = tmp;
}
return ( *this );
}
~test()
{
delete ptr;
}
You did not define a copy constructor. Like #jrok said, the compiler-generated default copy constructor only does shallow member-wise copy.
Your copy constructor can look something like:
public:
test(const test& t)
{
ptr = new int;
*ptr = *t.ptr;
}
BTW, you might want to define a destrcutor too to prevent memory leak.
~test()
{
delete ptr;
}

Class basic operators

Is it necessary to have a copy constructor, destructor and operator= in a class that have only static data member, no pointer
class myClass{
int dm;
public:
myClass(){
dm = 1;
}
~myClass(){ } // Is this line usefull ?
myClass(const myClass& myObj){ // and that operator?
this->dm = myObj.dm;
}
myClass& operator=(const myClass& myObj){ // and that one?
if(this != &myObj){
this->dm = myObj.dm;
}
return *this;
}
};
I read that the compiler build one for us, so it is better to not have one (when we add a data member we have to update the operators)
If you can't think of anything for the destructor to do, you almost certainly don't need to define it, with the exception of a virtual destructor in a base class. And if you don't need a destructor, you almost certainly don't need to define the copy constructor and assignment operator, and should not do so, as it's easy to get them wrong.
No. The compiler will automatically copy-construct, destruct, and assign all data members automatically. This means that if you have a class where they're all provided, you don't need to write these. I think it's better design to write operators on a per-member basis anyway.
The compiler would provide those itself. By default the copy constructor / assignment operator would copy / assign all members (and base parts of the object if the class is derived), and the destructor does nothing (invokes the destructor of all members / bases, but this is something you can't change even if you provide your own - that happens after the body of the user-defined destructor is exited).
So in this case it is completely unnecessary. All you can achieve is to introduce bugs in the code (e.g you add another member, but forget to update the copy constructor to copy that as well).
myClass& operator=(const myClass& myObj){ // and that one?
if(this != &myObj){
this->dm = myObj.dm;
}
return *this;
}
I don't think you should take "check for self-assignment" dogmatically. Does anything bad happen when you assign an int to itself (even indirectly)?
int a = 10;
int& ref = a;
a = ref;
Avoiding self-assignment is only relevant if the assignment operator first destroys the resources currently held and then creates new ones from the object on the right-hand side. Then it would be a disaster to find out that you have actually indirectly destroyed the right-hand object too.
But even in that case, it would be better to first create the copy of the right-hand side and then destroy the contents of the left-hand side, in which case self-assignment is no problem (except potentially inefficient). For a good way to implement this, Google for copy and swap idiom.