M.1 - Intro To Smart Pointers and Move Semantics - Learn C++

Download as pdf or txt
Download as pdf or txt
You are on page 1of 29

05/05/2021 M.

1 — Intro to smart pointers and move semantics | Learn C++

M.1 — Intro to smart pointers and move semantics


BY ALEX ON FEBRUARY 17TH, 2017 | LAST MODIFIED BY NASCARDRIVER ON APRIL 23RD, 2021

Consider a function in which we dynamically allocate a value:

1 void someFunction()
2 {
3 Resource *ptr = new Resource(); // Resource is a struct or class
4
5 // do stuff with ptr here
6
7 delete ptr;
8 }

Although the above code seems fairly straightforward, it’s fairly easy to forget to deallocate ptr. Even if you do
remember to delete ptr at the end of the function, there are a myriad of ways that ptr may not be deleted if the function
exits early. This can happen via an early return:

1 #include <iostream> Why smart pointer ?


2
3 void someFunction()
4 {
5 Resource *ptr = new Resource();
6
7 int x;
8 std::cout << "Enter an integer: ";
9 std::cin >> x;
10
11 if (x == 0)
12 return; // the function returns early, and ptr won’t be deleted!
13
14 // do stuff with ptr here
15
16 delete ptr;
17 }

or via a thrown exception:

1 #include <iostream>
2
3 void someFunction()
4 {
5 Resource *ptr = new Resource();
6
7 int x;
8 std::cout << "Enter an integer: ";
9 std::cin >> x;
10
11 if (x == 0)
12 throw 0; // the function returns early, and ptr won’t be deleted!
13
14 // do stuff with ptr here
15
16 delete ptr;
17 }

In the above two programs, the early return or throw statement execute, causing the function to terminate without
variable ptr being deleted. Consequently, the memory allocated for variable ptr is now leaked (and will be leaked again
every time this function is called and returns early).

At heart, these kinds of issues occur because pointer variables have no inherent mechanism to clean up after
themselves.

Smart pointer classes to the rescue?

https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 1/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
One of the best things about classes is that they contain destructors that automatically get executed when an object of
the class goes out of scope. So if you allocate (or acquire) memory in your constructor, you can deallocate it in your
destructor, and be guaranteed that the memory will be deallocated when the class object is destroyed (regardless of
whether it goes out of scope, gets explicitly deleted, etc…). This is at the heart of the RAII programming paradigm that
we talked about in lesson 11.9 -- Destructors.

So can we use a class to help us manage and clean up our pointers? We can!

Consider a class whose sole job was to hold and “own” a pointer passed to it, and then deallocate that pointer when
the class object went out of scope. As long as objects of that class were only created as local variables, we could
guarantee that the class would properly go out of scope (regardless of when or how our functions terminate) and the
owned pointer would get destroyed.

Here’s a first draft of the idea:

1 #include <iostream>
What is
2
3 template<class T>
RAII ?
4 class Auto_ptr1
5 {
6 T* m_ptr;
7 public:
8 // Pass in a pointer to "own" via the constructor
9 Auto_ptr1(T* ptr=nullptr)
10 :m_ptr(ptr)
11 {
12 }
13
14 // The destructor will make sure it gets deallocated
15 ~Auto_ptr1()
16 {
17 delete m_ptr;
18 }
19
20 // Overload dereference and operator-> so we can use Auto_ptr1 like m_ptr.
21 T& operator*() const { return *m_ptr; }
22 T* operator->() const { return m_ptr; }
23 };
24
25 // A sample class to prove the above works
26 class Resource
27 {
28 public:
29 Resource() { std::cout << "Resource acquired\n"; }
30 ~Resource() { std::cout << "Resource destroyed\n"; }
31 };
32
33 int main()
34 {
35 Auto_ptr1<Resource> res(new Resource()); // Note the allocation of memory here
36
37 // ... but no explicit delete needed
38
39 // Also note that the Resource in angled braces doesn't need a * symbol, since that's
supplied by the template
40
41 return 0;
42 } // res goes out of scope here, and destroys the allocated Resource for us

This program prints:

Resource acquired
Resource destroyed

Consider how this program and class work. First, we dynamically create a Resource, and pass it as a parameter to our
templated Auto_ptr1 class. From that point forward, our Auto_ptr1 variable res owns that Resource object (Auto_ptr1
has a composition relationship with m_ptr). Because res is declared as a local variable and has block scope, it will go

https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 2/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
out of scope when the block ends, and be destroyed (no worries about forgetting to deallocate it). And because it is a
class, when it is destroyed, the Auto_ptr1 destructor will be called. That destructor will ensure that the Resource
pointer it is holding gets deleted!

As long as Auto_ptr1 is defined as a local variable (with automatic duration, hence the “Auto” part of the class name),
the Resource will be guaranteed to be destroyed at the end of the block it is declared in, regardless of how the
function terminates (even if it terminates early).

Such a class is called a smart pointer. A Smart pointer is a composition class that is designed to manage dynamically
allocated memory and ensure that memory gets deleted when the smart pointer object goes out of scope. (Relatedly,
built-in pointers are sometimes called “dumb pointers” because they can’t clean up after themselves).

Now let’s go back to our someFunction() example above, and show how a smart pointer class can solve our
challenge:

1 #include <iostream>
2
3 template<class T> What is a smart
4 class Auto_ptr1
5 {
pointer ?
6 T* m_ptr;
7 public:
8 // Pass in a pointer to "own" via the constructor
9 Auto_ptr1(T* ptr=nullptr)
10 :m_ptr(ptr)
11 {
12 }
13
14 // The destructor will make sure it gets deallocated
15 ~Auto_ptr1()
16 {
17 delete m_ptr;
18 }
19
20 // Overload dereference and operator-> so we can use Auto_ptr1 like m_ptr.
21 T& operator*() const { return *m_ptr; }
22 T* operator->() const { return m_ptr; }
23 };
24
25 // A sample class to prove the above works
26 class Resource
27 {
28 public:
29 Resource() { std::cout << "Resource acquired\n"; }
30 ~Resource() { std::cout << "Resource destroyed\n"; }
31 void sayHi() { std::cout << "Hi!\n"; }
32 };
33
34 void someFunction()
35 {
36 Auto_ptr1<Resource> ptr(new Resource()); // ptr now owns the Resource
37
38 int x;
39 std::cout << "Enter an integer: ";
40 std::cin >> x;
41
42 if (x == 0)
43 return; // the function returns early
44
45 // do stuff with ptr here
46 ptr->sayHi();
47 }
48
49 int main()
50 {
51 someFunction();
52
53 return 0;
54 }

https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 3/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
If the user enters a non-zero integer, the above program will print:

Resource acquired
Hi!
Resource destroyed

If the user enters zero, the above program will terminate early, printing:

Resource acquired
Resource destroyed

Note that even in the case where the user enters zero and the function terminates early, the Resource is still properly
deallocated.

Because the ptr variable is a local variable, ptr will be destroyed when the function terminates (regardless of how it
terminates). And because the Auto_ptr1 destructor will clean up the Resource, we are assured that the Resource will
be properly cleaned up.

A critical flaw

The Auto_ptr1 class has a critical flaw lurking behind some auto-generated code. Before reading further, see if you
can identify what it is. We’ll wait…

(Hint: consider what parts of a class get auto-generated if you don’t supply them)

(Jeopardy music)

Okay, time’s up.

Rather than tell you, we’ll show you. Consider the following program:

1 #include <iostream>
2
3 // Same as above
4 template<class T>
5 class Auto_ptr1
6 {
7 T* m_ptr;
8 public:
9 Auto_ptr1(T* ptr=nullptr)
10 :m_ptr(ptr)
11 {
12 }
13
14 ~Auto_ptr1()
15 {
16 delete m_ptr;
17 }
18
19 T& operator*() const { return *m_ptr; }
20 T* operator->() const { return m_ptr; }
21 };
22
23 class Resource
24 {
25 public:
26 Resource() { std::cout << "Resource acquired\n"; }
27 ~Resource() { std::cout << "Resource destroyed\n"; }
28 };
29
30 int main()
31 {
32 Auto_ptr1<Resource> res1(new Resource());
33 Auto_ptr1<Resource> res2(res1); // Alternatively, don't initialize res2 and then assig
34 n res2 = res1;
35
https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 4/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
36 return 0;
}

This program prints:

Resource acquired
Resource destroyed
Resource destroyed

Very likely (but not necessarily) your program will crash at this point. See the problem now? Because we haven’t
supplied a copy constructor or an assignment operator, C++ provides one for us. And the functions it provides do
shallow copies. So when we initialize res2 with res1, both Auto_ptr1 variables are pointed at the same Resource.
When res2 goes out of the scope, it deletes the resource, leaving res1 with a dangling pointer. When res1 goes to
delete its (already deleted) Resource, crash!

You’d run into a similar problem with a function like this:

1 void passByValue(Auto_ptr1<Resource> res)


2 {
3 }
4
5 int main()
6 {
7 Auto_ptr1<Resource> res1(new Resource()); Critical flaw
8 passByValue(res1)
9
10 return 0;
11 }

In this program, res1 will be copied by value into passByValue’s parameter res, leading to duplication of the Resource
pointer. Crash!

So clearly this isn’t good. How can we address this?

Well, one thing we could do would be to explicitly define and delete the copy constructor and assignment operator,
thereby preventing any copies from being made in the first place. That would prevent the pass by value case (which is
good, we probably shouldn’t be passing these by value anyway).

But then how would we return an Auto_ptr1 from a function back to the caller?

1 ??? generateResource()
2 {
3 Resource *r = new Resource(); Solution
4 return Auto_ptr1(r);
5 }

We can’t return our Auto_ptr1 by reference, because the local Auto_ptr1 will be destroyed at the end of the function,
and the caller will be left with a dangling reference. Return by address has the same problem. We could return pointer
r by address, but then we might forget to delete r later, which is the whole point of using smart pointers in the first
place. So that’s out. Returning the Auto_ptr1 by value is the only option that makes sense -- but then we end up with
shallow copies, duplicated pointers, and crashes.

Another option would be to override the copy constructor and assignment operator to make deep copies. In this way,
we’d at least guarantee to avoid duplicate pointers to the same object. But copying can be expensive (and may not be
desirable or even possible), and we don’t want to make needless copies of objects just to return an Auto_ptr1 from a
function. Plus assigning or initializing a dumb pointer doesn’t copy the object being pointed to, so why would we
expect smart pointers to behave differently?

What do we do?

Move semantics

What if, instead of having our copy constructor and assignment operator copy the pointer (“copy semantics”), we
instead transfer/move ownership of the pointer from the source to the destination object? This is the core idea behind
move semantics. Move semantics means the class will transfer ownership of the object rather than making a copy.

https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 5/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
Let’s update our Auto_ptr1 class to show how this can be done:

1 #include <iostream>
2
3 template<class T>
4 class Auto_ptr2
5 {
6 T* m_ptr;
7 public:
8 Auto_ptr2(T* ptr=nullptr)
9 :m_ptr(ptr) Copy constructor and assignment
10 {
11 } operator implements move semantics
12
13 ~Auto_ptr2()
14 {
15 delete m_ptr;
16 }
17
18 // A copy constructor that implements move semantics
19 Auto_ptr2(Auto_ptr2& a) // note: not const
20 {
21 m_ptr = a.m_ptr; // transfer our dumb pointer from the source to our local object
22 a.m_ptr = nullptr; // make sure the source no longer owns the pointer
23 }
24
25 // An assignment operator that implements move semantics
26 Auto_ptr2& operator=(Auto_ptr2& a) // note: not const
27 {
28 if (&a == this)
29 return *this;
30
31 delete m_ptr; // make sure we deallocate any pointer the destination is already ho
32 lding first
33 m_ptr = a.m_ptr; // then transfer our dumb pointer from the source to the local ob
34 ject
35 a.m_ptr = nullptr; // make sure the source no longer owns the pointer
36 return *this;
37 }
38
39 T& operator*() const { return *m_ptr; }
40 T* operator->() const { return m_ptr; }
41 bool isNull() const { return m_ptr == nullptr; }
42 };
43
44 class Resource
45 {
46 public:
47 Resource() { std::cout << "Resource acquired\n"; }
48 ~Resource() { std::cout << "Resource destroyed\n"; }
49 };
50
51 int main()
52 {
53 Auto_ptr2<Resource> res1(new Resource());
54 Auto_ptr2<Resource> res2; // Start as nullptr
55
56 std::cout << "res1 is " << (res1.isNull() ? "null\n" : "not null\n");
57 std::cout << "res2 is " << (res2.isNull() ? "null\n" : "not null\n");
58
59 res2 = res1; // res2 assumes ownership, res1 is set to null
60
61 std::cout << "Ownership transferred\n";
62
63 std::cout << "res1 is " << (res1.isNull() ? "null\n" : "not null\n");
64 std::cout << "res2 is " << (res2.isNull() ? "null\n" : "not null\n");
65
return 0;
}

This program prints:


https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 6/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
Resource acquired
res1 is not null
res2 is null
Ownership transferred
res1 is null
res2 is not null
Resource destroyed

Note that our overloaded operator= gave ownership of m_ptr from res1 to res2! Consequently, we don’t end up with
duplicate copies of the pointer, and everything gets tidily cleaned up.

std::auto_ptr, and why it was a bad idea

Now would be an appropriate time to talk about std::auto_ptr. std::auto_ptr, introduced in C++98 and removed in
C++17, was C++’s first attempt at a standardized smart pointer. std::auto_ptr opted to implement move semantics just
like the Auto_ptr2 class does.

However, std::auto_ptr (and our Auto_ptr2 class) has a number of problems that makes using it dangerous.

First, because std::auto_ptr implements move semantics through the copy constructor and assignment operator,
passing a std::auto_ptr by value to a function will cause your resource to get moved to the function parameter (and be
destroyed at the end of the function when the function parameters go out of scope). Then when you go to access your
auto_ptr argument from the caller (not realizing it was transferred and deleted), you’re suddenly dereferencing a null
pointer. Crash!

Second, std::auto_ptr always deletes its contents using non-array delete. This means auto_ptr won’t work correctly
with dynamically allocated arrays, because it uses the wrong kind of deallocation. Worse, it won’t prevent you from
passing it a dynamic array, which it will then mismanage, leading to memory leaks.

Finally, auto_ptr doesn’t play nice with a lot of the other classes in the standard library, including most of the
containers and algorithms. This occurs because those standard library classes assume that when they copy an item, it
actually makes a copy, not a move.

Because of the above mentioned shortcomings, std::auto_ptr has been deprecated in C++11 and removed in C++17.

Moving forward

The core problem with the design of std::auto_ptr is that prior to C++11, the C++ language simply had no mechanism
to differentiate “copy semantics” from “move semantics”. Overriding the copy semantics to implement move semantics
leads to weird edge cases and inadvertent bugs. For example, you can write res1 = res2 and have no idea
whether res2 will be changed or not!

Because of this, in C++11, the concept of “move” was formally defined, and “move semantics” were added to the
language to properly differentiate copying from moving. Now that we’ve set the stage for why move semantics can be
useful, we’ll explore the topic of move semantics throughout the rest of this chapter. We’ll also fix our Auto_ptr2 class
using move semantics.

In C++11, std::auto_ptr has been replaced by a bunch of other types of “move-aware” smart pointers: std::unique_ptr,
std::weak_ptr, and std::shared_ptr. We’ll also explore the two most popular of these: unique_ptr (which is a direct
replacement for auto_ptr) and shared_ptr.

M.2 -- R-value references

Index

https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 7/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++

20.x -- Chapter 20 comprehensive quiz

C++ TUTORIAL | PRINT THIS POST

159 comments to M.1 — Intro to smart pointers and move semantics

« Older Comments 1 2

kio
April 21, 2021 at 1:50 pm · Reply

Alex, this part should be put in the green box, because it's rule:
Rule: std::auto_ptr is deprecated and should not be used. (Use std::unique_ptr or std::shared_ptr
instead)..

nascardriver
April 23, 2021 at 5:31 am · Reply

Rule removed. It's no longer relevant, because `std::auto_ptr` doesn't exist anymore.

Thanks for pointing it out!

Karthik
March 14, 2021 at 8:34 am · Reply

Hello,

1 int main()
2 {
3 Auto_ptr1<Resource> res1(new Resource());
4 Auto_ptr1<Resource> res2(res1); // Alternatively, don't initialize res2 and then ass
ign res2 = res1;
5
6 return 0;
7 }

This program prints:

Resource acquired
Resource destroyed
Resource destroyed

Why is "Resource acquired" only printed once? Why Auto_ptr1<Resource> res2 variable does not call the
constructor of Resource?

Alex
March 15, 2021 at 10:05 am · Reply

Correct. In the example you're referencing, we haven't supplied a copy constructor, so C++
will provide one for us that does a member-wise copy. Because m_ptr is a pointer, this does a
shallow copy.

Manuel
March 3, 2021 at 10:48 am · Reply

https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 8/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
Hey Alex and NascarDriver!
Thank you for the great lessons, you have a great way of teaching the subject!

There's a small typo I'd like to point out in line 46 of the 5th block of code:

1 ptr->sayHi();

This line should be:

1 res->sayHi();

Thanks again for the great lessons!

yeokaiwei
January 6, 2021 at 11:18 pm · Reply

Dear Alex,
May I ask where the topic on Modules are?

nascardriver
January 19, 2021 at 5:52 am · Reply

There is no lesson about modules yet, neither is there a compiler that supports modules

yeokaiwei
January 20, 2021 at 5:50 am · Reply

@nascardriver

Oh, I was looking forward to not having to forward declare and #include libraries in any order I wanted.

Do you know when will the next C++ Programming Language 5th Edition will be out?

nascardriver
January 20, 2021 at 6:42 am · Reply

I know nothing about books, I don't like books, they get outdated.

I don't think modules will save you any time compared to the current preprocessor way. They'll
allow you to selectively include parts of a module, but I suppose you'll have to type more.

Kurt Van Bever


December 3, 2020 at 2:50 am · Reply

I read through this comment section but I'm still confused regarding this line here :

1 T* operator->() const { return m_ptr; }

I have 2 questions :

1: how exactly does operator-> get transferred to m_ptr. What goes on under the hood there?

2: then what would you do if you actually really wanted to overload operator-> so that it just returned m_ptr?

nascardriver
December 5, 2020 at 6:23 am · Reply

I don't understand your questions.


`operator->()` just returns `m_ptr`, there are no secrets.
If it helps, you can call operators just like regular functions

https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 9/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
1 my_auto_ptr.operator->()->something();

Constantinos
October 14, 2020 at 1:00 pm · Reply

Hey,

I would like to make a general question concerning the following line of code:

1 Auto_ptr1<Resource> res(new Resource);

I understand that by "res(new Resource)" we call the constructor of the Auto_ptr1 class to instantiate its "res"
object.

When we write "new Resource", this means that we are dynamically allocating memory for a Resource type object,
and we do that by calling the Resource class constructor this time. Is this right? If yes, shouldn't we write it as "new
Resource()" (i.e., with parenthesis like in "res(...)")? I tried to run the same example by just adding the parenthesis
and produced the same output. Are those two expressions (with and without parenthesis) the same and/or I am
missing something?

Thanks for this nice tutorial!

Constantinos

Constantinos
October 17, 2020 at 2:59 pm · Reply

??

nascardriver
October 18, 2020 at 1:50 am · Reply

If `Resource` is a class-type, the two are the same. Otherwise the allocated object is
uninitialized in case of `new Resource`.

Constantinos
October 18, 2020 at 3:10 am · Reply

Thanks!

F. Wu
August 31, 2020 at 8:19 pm · Reply

Using your Auto_ptr1 class, I managed to create a pointer that points to a smart pointer, and then I
assigned that pointer to a smart pointer:

Auto_ptr1<int> *ptr { new Auto_ptr1<int>{ new int{ 5 } } };


auto smartPointer { ptr };

goiu
August 4, 2020 at 11:52 pm · Reply

1 Auto_ptr1<Resource> res(new Resource); // Note the allocation of memory here


2
3 // ...
4
5 // Also note that the Resource in angled braces doesn't need a * symbol, since tha
t's supplied by the template
https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 10/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
so if within the angled braces we put Resource* then res would hold a pointer to a pointer to a Resource object?

nascardriver
August 8, 2020 at 1:49 am · Reply

It would hold a pointer to a `Resource` pointer. What that pointer points to is up to how you
initialize it. Upon destruction, only the pointer would be freed, not the object the pointer points
to.

Chaitanya Prasad
July 20, 2020 at 3:33 am · Reply

I am a bit confused with the given code.


this is the smart pointer code given here

1 template<class T>
2 class Auto_ptr2
3 {
4 T* m_ptr;
5 public:
6 Auto_ptr2(T* ptr=nullptr)
7 :m_ptr(ptr)
8 {
9 }
10
11 ~Auto_ptr2()
12 {
13 delete m_ptr;
14 }
15
16 // A copy constructor that implements move semantics
17 Auto_ptr2(Auto_ptr2& a) // note: not const
18 {
19 m_ptr = a.m_ptr; // transfer our dumb pointer from the source to our local obje
20 ct
21 a.m_ptr = nullptr; // make sure the source no longer owns the pointer
22 }
23
24 // An assignment operator that implements move semantics
25 Auto_ptr2& operator=(Auto_ptr2& a) // note: not const
26 {
27 if (&a == this)
28 return *this;
29
30 delete m_ptr; // make sure we deallocate any pointer the destination is already
31 holding first
32 m_ptr = a.m_ptr; // then transfer our dumb pointer from the source to the local
33 object
34 a.m_ptr = nullptr; // make sure the source no longer owns the pointer
35 return *this;
36 }
37
38 T& operator*() const { return *m_ptr; }
T* operator->() const { return m_ptr; }
bool isNull() const { return m_ptr == nullptr; }
};

In this in the copy constructor and assignment operator we are copying the value of the sent argument to the
current one and making the argument value point to null. So here a transfer of ownership is taking place.
But just below that an issue is mentioned that if the object inside the function goes out of scope it will be destroyed
and the object in the caller part will be left with dangling pointer. But, here we can see that the ownership is being
transferred so how is this issue happening.

Just in the next lesson for the move constructor and move assignment a double reference is used in the argument.
How is it solving the problem if it did occur earlier and how is it different from before?

https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 11/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
1
2 Auto_ptr4(Auto_ptr4&& a)
3 : m_ptr(a.m_ptr)
4 {
5 a.m_ptr = nullptr; // we'll talk more about this line below
6 }
7 Auto_ptr4& operator=(Auto_ptr4&& a)
8 {
9 // Self-assignment detection
10 if (&a == this)
11 return *this;
12
13 // Release any resource we're holding
14 delete m_ptr;
15
16 // Transfer ownership of a.m_ptr to m_ptr
17 m_ptr = a.m_ptr;
18 a.m_ptr = nullptr; // we'll talk more about this line below
19
20 return *this;
}

nascardriver
July 20, 2020 at 4:37 am · Reply

When you pass the Auto_ptr to a function, that function will take ownership of the resource,
even if the caller doesn't want this to happen.
The function then destroys the resource and the caller can't use it anymore.
By using r-value references, the Auto_ptr is only moved if the argument would become inaccessible
anyway, or if it is explicitly moved by the caller using `std::move()` (covered later). That makes it obvious to
the caller that they no longer own the resource and shouldn't access it.

Chaitanya Prasad
July 20, 2020 at 6:06 am · Reply

but in the two given assignment operator overloaded versions the body of both the
functions are performing the same operation. The how are they different?

1 Auto_ptr4& operator=(Auto_ptr4&& a)
2 {
3 // Self-assignment detection
4 if (&a == this)
5 return *this;
6
7 // Release any resource we're holding
8 delete m_ptr;
9
10 // Transfer ownership of a.m_ptr to m_ptr
11 m_ptr = a.m_ptr;
12 a.m_ptr = nullptr; // we'll talk more about this line below
13
14 return *this;
15 }

and

1 Auto_ptr2& operator=(Auto_ptr2& a) // note: not const


2 {
3 if (&a == this)
4 return *this;
5
6 delete m_ptr; // make sure we deallocate any pointer the destination
is already holding first
7 m_ptr = a.m_ptr; // then transfer our dumb pointer from the source to
the local object
8 a.m_ptr = nullptr; // make sure the source no longer owns the pointer
9 return *this;
10 }
https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 12/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
11
12 T& operator*() const { return *m_ptr; }
13 T* operator->() const { return m_ptr; }
14 bool isNull() const { return m_ptr == nullptr; }

nascardriver
July 20, 2020 at 6:12 am · Reply

The functions do the same, but they're invoked for different reasons. The real move
assignment operator is only invoked if the caller wants to give up ownership of the
resource. In that case it's fine to steal the resource.
The copy assignment operator is invoked when the caller wants to create a copy. But if this
operator steals the resource, the caller will be confused.

Chaitanya Prasad
July 20, 2020 at 6:29 am · Reply

so you mean to say the function "Auto_ptr2& operator=(Auto_ptr2& a)" is


supposed to be performing a copy whereas "Auto_ptr4& operator=
(Auto_ptr4&& a)" this will perform a move.
The point where i am confused is that since both are doing the same operation in their
bodies and then making the argument variable point to null then how is the difference been
made. one has a reference and another a rvalue reference in it's argument. how does this
make both the functions behave differently?

nascardriver
July 20, 2020 at 6:48 am · Reply

1 a = something(); // move assignment (1)


2 a = std::move(b); // move assignment (2)
3 a = b; // copy assignment (3)

If (1) and (2) steal the resource, that's fine. If (3) steals the resource, that's bad.

Chaitanya Prasad
July 20, 2020 at 7:01 am · Reply

ok..let me go through it again..can you elaborate the explanation as i


still did not understand the issue here

Max
November 30, 2020 at 11:29 am · Reply

Hi, the key point of the issiue is that by using move semantics
you state clearely that you're aware you're giving up your
resource by using std::move():

1 a = std::move(b); // std::move means you know you're givi


2 ng up b to a
3
a = b ; // here you don't know if you're making a copy of
b or giving it up to a

So move semantics is more about a programmer being aware of moving his


resources rather than copying them. The core magic happens not inside the
copy assignment operator, but at the line where the assignment takes place:

https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 13/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
1 a = std::move(b);

saj
June 29, 2020 at 4:29 am · Reply

Suppose we track the creator of Auto_ptr1 object with the help of additional member variable, and let
only the creator to delete pointer member like this:

1 #include <iostream>
2
3 template<class T>
4 class Auto_ptr1
5 {
6 T* m_ptr;
7 bool m_isCreator;
8
9 public:
10
11 Auto_ptr1(T* ptr=nullptr)
12 :m_ptr(ptr), m_isCreator(true)
13 {
14 }
15
16 // If the object is creator, The destructor will make sure m_ptr gets deallocated
17 ~Auto_ptr1()
18 {
19 if (m_isCreator) delete m_ptr;
20 }
21
22 //remove the ability of new object (created by copy constructor and assignment oper
ator) from deleting the pointer
23 Auto_ptr1(const Auto_ptr1& copyFrom) : m_ptr(copyFrom.m_ptr), m_isCreator(false)
24 {
25 }
26
27 Auto_ptr1& operator=(const Auto_ptr1& rhs)
28 {
29 m_ptr = rhs.m_ptr;
30 m_isCreator = false;
31 return *this;
32 }
33
34 // Overload dereference and operator-> so we can use Auto_ptr1 like m_ptr.
35 T& operator*() const { return *m_ptr; }
36 T* operator->() const { return m_ptr; }
37 };
38
39
40
41
42
43 // A sample class to prove the above works
44 class Resource
45 {
46 public:
47 Resource() { std::cout << "Resource acquired\n"; }
48 ~Resource() { std::cout << "Resource destroyed\n"; }
49 };
50
51
52 void passByValue(Auto_ptr1<Resource> res2)
53 {
54 std::cout << "inside passByValue()\n";
55
56 Auto_ptr1<Resource> res3 = res2;
57 std::cout << "assignment operator was used\n";
58 }
59
60 int main()
https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 14/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
61 {
62 Auto_ptr1<Resource> res(new Resource);
63 passByValue(res);
64 return 0;
65 }

Is there any downside to this approach?

nascardriver
July 4, 2020 at 12:11 am · Reply

1 Auto_ptr1<Resource> table{};
2
3 {
4 Auto_ptr1<Resource> chair{ new Resource{} };
5 table = chair;
6 }
7
8 *table; // Undefined behavior, @table is dangling.

This particular problem could be solved by using a adding constructor and move-assignment operator.
However, you'll run into a situation where 2 `Auto_ptr1`'s use the resource and you don't know which one is
going to die first.

saj
July 8, 2020 at 9:03 pm · Reply

You're right, after knowing about shared and weak pointers, I just realized how stupid my
question was. I also forgot to delete old allocated memory inside operator=(), I see how
easy it is to forget things for a newbie cpp programmer like me.

anyways, Many thanks to you nascardriver for your detailed answers to all my stupid questions, I
appreciate all your help in comment section a lot

Revester
April 20, 2020 at 11:43 am · Reply

"Finally, auto_ptr doesn’t play nice with a lot of the other classes in the standard library, including
most of the containers and algorithms. This occurs because those standard library classes assume
that when they copy an item, it actually makes a copy, not does a move."
"...makes a copy, not does a move" sounds silly and I think it's just wrong.
I think it should be just "makes a copy, not a move".

nascardriver
April 25, 2020 at 3:45 am · Reply

That sounded silly, lesson updated, thanks!

Ged
March 9, 2020 at 3:51 am · Reply

How does this line work? You said that we don't need * because it is supported by the template. How
does it know to allocate memory for m_ptr? I've created 2 pointers but it only destroys it once, not
twice.

1 Auto_ptr1<Resource> res{ new Resource };

1 #include <iostream>
2
https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 15/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
3 template<class T>
4 class Auto_ptr1
5 {
6 T* m_ptr;
7 T* m_ptr2;
8 public:
9 // Pass in a pointer to "own" via the constructor
10 Auto_ptr1(T* ptr = nullptr, T* ptr2 = nullptr)
11 :m_ptr(ptr), m_ptr2(ptr2)
12 {
13 }
14
15 // The destructor will make sure it gets deallocated
16 ~Auto_ptr1()
17 {
18 delete m_ptr;
19 delete m_ptr2;
20 }
21
22 // Overload dereference and operator-> so we can use Auto_ptr1 like m_ptr.
23 T& operator*() const { return *m_ptr; }
24 T* operator->() const { return m_ptr; }
25 };
26
27 // A sample class to prove the above works
28 class Resource
29 {
30 public:
31 Resource() { std::cout << "Resource acquired\n"; }
32 ~Resource() { std::cout << "Resource destroyed\n"; }
33 };
34
35 int main()
36 {
37 Auto_ptr1<Resource> res{ new Resource }; // Note the allocation of memory here
38
39 // ... but no explicit delete needed
40
41 // Also note that the Resource in angled braces doesn't need a * symbol, since tha
t's supplied by the template
42
43 return 0;
44 } // res goes

Ged
March 9, 2020 at 4:56 am · Reply

I juts found out the answer on my own. For some reason I totally forgot that we are using a
constructor.

1 Auto_ptr1<Resource> res{ new Resource, new Resource };

This works, never seen "new" being used like that, that's why it took me some time to understand.

saj
June 29, 2020 at 3:16 am · Reply

Thanks for clearing that up, This line really had me scratching my head until i saw your
response.

Naman Jain
February 26, 2020 at 11:32 pm · Reply

What is a "non-array delete"?

https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 16/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++

Alex
March 7, 2020 at 9:04 am · Reply

Using "delete" instead of "delete[]"

David
February 11, 2020 at 8:51 am · Reply

Hello,I have a question in the following program.I have make the isNull() public,but the compiler
always says it can not find the isNull() member function.Could you point out where something go
wrong?
Thanks for replying.

1 #include<iostream>
2
3 template<typename T>
4 class Auto_ptr1
5 {
6 T* m_ptr;
7 public:
8 Auto_ptr1(T* ptr = nullptr) :m_ptr{ ptr }
9 {}
10 ~Auto_ptr1()
11 {
12 delete m_ptr;
13 }
14
15
16 Auto_ptr1(Auto_ptr1& a)
17 {
18 m_ptr = a.m_ptr;
19 a.m_ptr = nullptr;
20 }
21
22 Auto_ptr1& operatpr = (Auto_ptr1 & a)
23 {
24 if (&a == this)
25 return *this;
26
27 delete m_ptr;
28 m_ptr = a.m_ptr;
29 a.m_ptr = nullptr;
30 return *this;
31 }
32
33 bool isNull()const
34 {
35 return m_ptr == nullptr;
36 }
37 T& operator*()const
38 {
39 return *m_ptr;
40 }
41
42 T* operator->()const
43 {
44 return m_ptr;
45 }
46
47 };
48
49 class Resource
50 {
51 public:
52 Resource()
53 {
54 std::cout << "Resource acquired\n";
https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 17/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
55 }
56
57 ~Resource()
58 {
59 std::cout << "Resource destroyed\n";
60 }
61
62
63 void sayHi()
64 {
65 std::cout << "Say Hi~\n";
66 }
67
68 };
69
70
71 int main()
72 {
73 Auto_ptr1<Resource> res1(new Resource);
74 Auto_ptr1<Resource> res2; // Start as nullptr
75 std::cout << "res1 is " << (res1.isNull() ? "null\n" : "not null\n");
76 std::cout << "res2 is " << (res2.isNull() ? "null\n" : "not null\n");
77 res2 = res1; // res2 assumes ownership, res1 is set to null
78 std::cout << "Ownership transferred\n";
79 std::cout << "res1 is " << (res1.isNull() ? "null\n" : "not null\n");
80 std::cout << "res2 is " << (res2.isNull() ? "null\n" : "not null\n");
81 return 0;
82 }

nascardriver
February 13, 2020 at 7:08 am · Reply

You misspelled "operator".

ChubbyBunny
November 30, 2019 at 8:01 pm · Reply

Hi Alex,

By std::scoped_ptr you actually mean boost::scoped_ptr?

nascardriver
December 2, 2019 at 3:56 am · Reply

`scoped_ptr` never made it into the standard, I removed it from the lesson since learncpp
doesn't teach libraries. Thanks for pointing it out!

Parsa
October 20, 2019 at 1:03 pm · Reply

The sayHi() function prints "Hi" then new line. In the output example it printed "Hi!" then new line

Alex
October 22, 2019 at 6:28 pm · Reply

The lesson now has 20% more enthusiasm. Thanks!

Parsa
October 20, 2019 at 12:36 pm · Reply

https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 18/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
The operator-> overload seems a bit confusing. Was there a tutorial on it that I missed?

Nevermind. I think I understand.

cdecde57
August 20, 2019 at 7:27 pm · Reply

Hello! BTW the lessons have been great! I have been learning from this place sense I was interested
in c++ and really this is the only place I have learned things. It is amazing and thanks!

Anyways I am curious about the smart pointer class we made.

I understand how it works and how it dealocates itself. At the end of this lesson it talks about things like
std::unique_ptr and I have found that it is very similar to the class were making. Are we making the class to really
explore the idea of move semantics and really whats going on behind things like std::unique_ptr or is the class
something completely different?

I guess what I am asking is do I need to memorize how to create this class and everything, say because there is
not one that works similarly to it in the standard library or do we just need to learn it and understand it so we can
better understand pointers and the semantics of what you can actually do with them when implemented into
classes and things like that in general?

Thanks!

nascardriver
August 21, 2019 at 5:24 am · Reply

Like a lot of things, this class can (and should be) replaced with something from the standard
library (In this case, a smart pointer from <memory>).
It's important to understand how such a class can be implemented by hand, because you'll have to do it at
some point (Not necessarily an existing class, but you need to know it for custom classes too).

Alex
August 21, 2019 at 8:36 pm · Reply

These classes are discussed to explore the ideas behind move semantics, starting from
concepts we've already discussed (and why they don't work well in this case). This sets up the
rationale for the new language mechanics that we discuss in subsequent lessons (e.g. rvalue references).
You don't need to memorize these classes -- you should prefer the standard library versions instead
(excluding std::auto_ptr).

cdecde57
August 22, 2019 at 12:18 pm · Reply

Thanks for the reply :D

Michael Song
July 28, 2019 at 9:07 pm · Reply

Hi!
" We could return pointer r by address, but then we might forget to delete r later, which is the whole
point of using smart pointers in the first place. "
Was it meant to be "return pointer r by value"? Because if we return r by address, then r will go out of scope when
the function ends and the address will be pointing to nothing.

Alex

https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 19/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
July 29, 2019 at 10:32 am · Reply

If we return the value pointer r points to by value, then r won't get deleted and we'll leak
memory.

Michael Song
July 29, 2019 at 9:36 pm · Reply

Thanks, Alex. I can get what you mean now.


What if we don't return the value the pointer points to by value but return the pointer itself
by value instead?
Is returning the pointer itself by value same as returning the value the pointer points to by address?
The pointer r holds the address of the memory for Resource as its value and if we return that to
another pointer. That pointer will point to the memory for Resource and r can safely go out of scope
without a memory leak.
Does it work this way?

Alex
August 3, 2019 at 3:25 pm · Reply

Yes, returning the pointer itself by value is the same as returning the value the point
points to by address.

So yes, you could return r by address -- local variable r would go out of scope, but the address
returned could be assigned to another pointer that's in scope for the caller.

But then the caller is responsible for deleting the resource, which means we could end up with
memory leaks. Fixing this is a big part of what the lessons in this chapter are about.

Michael Song
August 4, 2019 at 1:33 am · Reply

Thank you Alex!

Azad
June 20, 2019 at 8:20 pm · Reply

Auto_Ptr2, line 19, 26 wont the type be written as Auto_ptr2<T>& instead of just Auto_Ptr2& ?

nascardriver
June 21, 2019 at 12:53 am · Reply

Hi!

The template argument can be omitted, because the injected-class-name is the class name along with the
class' template arguments.
If you add the arguments to every use of `Auto_ptr2` inside the class, you'll have extra work if you ever
decide to add or remove template parameters from the class later on.

I don't think @Alex covered this.

X
May 30, 2019 at 7:27 am · Reply

Can you explain why there is a const keyword in these statements? I've seen them twice in the same
function stub and I don't understand why some of them come later in the statement (like these) rather
than at the beginning. What is the difference between the two when you have two consts in the same stub and one
https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 20/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
comes before the T and one is in the spot it is at now? Basically, what I mean is something like "const T& operator*
() const {return *m_ptr;}" I'm not sure if that is valid but I have seen you guys write code in that exact style and that
is what I am confused about.

And for the second piece of code in Main - why is the first line non-null? What makes it non-null? What value does
Resource have? Also, what is <Resource> doing here? What is its purpose?

1 T& operator*() const { return *m_ptr; }


2 T* operator->() const { return m_ptr; }
3 bool isNull() const { return m_ptr == nullptr; }
4
5 int main()
6 {
7 Auto_ptr2<Resource> res1(new Resource);
8 Auto_ptr2<Resource> res2; // Start as nullptr
9 }

nascardriver
May 30, 2019 at 7:30 am · Reply

Lesson 8.10.

1 // vvvvv The return type is const


2 const int &fn(void) const;
3 // ^^^^^ The function is const

X
May 30, 2019 at 7:37 am · Reply

Ohhh that makes a lot more sense thanks. Can you answer the second paragraph of
questions for me as well?

nascardriver
May 30, 2019 at 7:50 am · Reply

`<Resource>` is a template argument, see Chapter 13.


`new Resouce` allocates a new `Resource`, returns its pointer and passes it to
`Auto_ptr2`'s constructor. Lesson 6.9, 8.5.

X
May 30, 2019 at 8:27 am · Reply

Extremely helpful. Much appreciated.

X
May 30, 2019 at 8:32 am · Reply

One last question on this topic.

1 Auto_ptr2& operator=(Auto_ptr2& a) // note: not const


2 {
3 if (&a == this)
4 return *this;
5
6 delete m_ptr; // make sure we deallocate any pointer the dest
ination is already holding first
7 m_ptr = a.m_ptr; // then transfer our dumb pointer from the s
ource to the local object
8 a.m_ptr = nullptr; // make sure the source no longer owns the
9 pointer

https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 21/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
10 return *this;
}

Is this the code that returns the pointer to be passed into the constructor?

nascardriver
May 30, 2019 at 8:34 am · Reply

No, this is unrelated. You can't understand these lessons if you haven't
read the preceding ones.

X
May 30, 2019 at 10:39 am · Reply

I've read every topic thus far. I just haven't retained everything. But I just
looked through the code again and with your help, I finally understand
what's going on.

Amrita
May 24, 2019 at 5:58 am · Reply

Hi Alex
Under the move semantics section, line 31 :

delete m_ptr;

I was expecting that this line will invoke the destructor of the Resource class. But it does not happen. Can you
please explain?

Regards
Amrita

nascardriver
May 24, 2019 at 7:51 am · Reply

it does. what makes you think it doesn't?

Amrita
May 26, 2019 at 2:07 am · Reply

"Resource destroyed" does not get printed when the overloaded assignment operator is
invoked

nascardriver
May 26, 2019 at 2:10 am · Reply

Line 57?
@res2 doesn't hold a resource. @m_ptr is a null pointer. If no object exists, no
object can be destroyed.

Amrita
May 26, 2019 at 6:04 am · Reply

Now I get it. Thanks :)

https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 22/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++

Anthony
May 18, 2019 at 5:40 am · Reply

Am I correct in the saying that, really, the only difference in behaviour between 'default copy' and
move semantics as far as the @Auto_ptr2 class is concerned, is just the assignment of the source
pointer to nullptr (so that the memory to which it used to point won't be destructed when the source goes out of
scope)?

nascardriver
May 18, 2019 at 6:14 am · Reply

Yes

Anthony
May 18, 2019 at 1:40 pm · Reply

Thanks :)

Is it also true to say that the destructor associated with the object pointed to by Auto_ptr2's mptr will be
triggered when Auto_ptr2 is itself destructed because it goes out of scope, even if we have 'moved' a
different object into that pointer?

nascardriver
May 18, 2019 at 10:59 pm · Reply

When a @Auto_ptr2 goes out of scope, it's destructor is fired. The destructor calls
@delete on the currently managed object, causing its destructor to fire as well.
If another pointer was moved into out @Auto_ptr2, then the old managed pointer will be deleted
by @operator=, causing the old object to be destructed.
If this isn't what you were asking, try posting an example of what you mean.

Anthony
May 19, 2019 at 10:21 am · Reply

Hi,

Yep, that's exactly what I was asking. I've summarized what I think is correct in the code
comments below. Could you check what I've commented about the different versions of the
assignment operator. I'm pretty sure it's all correct.

Much appreciated - as always!

1 #include <iostream>
2
3 template <class T>
4 class Auto_ptr {
5 private:
6 T* m_ptr; // The payload pointer
7 int m_id;
8 public:
9 Auto_ptr(T* ptr = nullptr) : m_ptr{ ptr } {
10 createId();
11 std::cerr << "Auto_ptr " << getId() << " constructed (norma
12 l)\n";
13 }
14 /* The copy constructor is now a move constructor. The only rea
15 l difference
16 is that the payload pointer, ap.m_ptr, is set to nullptr. Th
17 is
18 determines how arguments are passed to and returned from fun
19 ctions - in
https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 23/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
20 fact, anywhere a copy would have been made */
21 Auto_ptr(Auto_ptr<T>& ap) {
22 createId();
23 m_ptr = ap.m_ptr;
24 ap.m_ptr = nullptr;
25 std::cerr << "Auto_ptr " << getId() << " constructed (move)
26 \n";
27 }
28
29 /* The assignment is a move operation. Arguments are passed in
30 and returned
31 by reference. This is efficient and is the usual way of doin
32 g things */
33 Auto_ptr<T>& operator= (Auto_ptr<T>& ap) {
34
35 /* In this alternative version, arguments are passed in by valu
36 e and
37 returned by reference. It works, but an unnecessary copy is
38 created as
39 the input parameter. The payload pointer is passed through t
40 he copy on
41 its way to the intended assignee object. Though the copy is
42 destructed
43 on exit from the function, it no longer owns the payload, so
44 the
45 payload, which is owned by the assignee, is safe. */
46 //Auto_ptr<T>& operator= (Auto_ptr<T> ap) {
47
48 /* In this alternative version, which is flawed, arguments are
49 passed in by
50 reference and returned by value. The payload pointer is tran
51 sferred to
52 the intended assignee object, but is then immediately forwar
53 ded to an
54 unnecessary copy that's created for the return. The assignee
55 no longer
56 owns the payload! As the funtion exits, the return copy, whi
57 ch is a
58 temporary object, destructs, destructing the payload along w
59 ith it! */
60 //Auto_ptr<T> operator= (Auto_ptr<T>& ap) {
61
62 /* In this alternative version, which is flawed, arguments are
63 passed in
64 and returned by value. An unnecessary copy is created as the
65 input
66 parameter. The payload pointer is passed through the copy on
67 its way to
68 the intended assignee object, but is then immediately forwar
69 ded to yet
70 another unnecessary copy that's created for the return. The
71 assignee no
72 longer owns the payload! The input copy is destructed on exi
73 t from the
74 function. The return copy, which is a also a temporary objec
75 t, also
76 destructs, destructing the payload along with it! */
77 //Auto_ptr<T> operator= (Auto_ptr<T> ap) {
78
79 /* In all versions, the input parameter can't be made const bec
80 ause we
81 modify ap.m_ptr by setting it to nullptr (not to mention tha
82 t we also
83 invoke createId()), which also means that we can't pass in t
84 emporary
85 objects */
86 if ((void*)this == (void*)& ap) {
87 return *this;
88 }
89 if (m_ptr)
90 delete m_ptr;

https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 24/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
91 std::cerr << "Auto_ptr " << ap.getId() << ", which owned Pay
92 load "
93 << ap->getId() << ", ";
94 m_ptr = ap.m_ptr;
95 ap.m_ptr = nullptr;
96 std::cerr << "is assigned to Auto_ptr " << this->getId()
97 << ", which now owns Payload " << (*this)->getId()
98 << "\n";
99 return *this;
100 }
101 ~Auto_ptr() {
102 delete m_ptr;
103 std::cerr << "Auto_ptr " << getId() << " destructed\n";
104 }
105 T* operator->() const { // I still don't understand this..
106 return m_ptr;
107 }
108 T& operator*() const { // This is straightforward
109 return *m_ptr;
110 }
111 void createId() {
112 static int idGen = 0;
113 m_id = idGen++;
114 }
115 int getId() const { return m_id; }
116 };
117
118 class Payload {
119 private:
120 int m_val;
121 int m_id;
public:
Payload(int val) : m_val{ val } {
createId();
std::cerr << "Payload " << getId() << " constructed (normal)
\n";
}
~Payload() {
std::cerr << "Payload " << getId() << " destructed\n";
}
void createId() {
static int idGen = 0;
m_id = idGen++;
}
int getId() const { return m_id; }
int getVal() const { return m_val; }
};

Auto_ptr<Payload>& func(Auto_ptr<Payload>& ap) { return ap; }

int main() {
Auto_ptr<Payload> ap0{ new Payload{9} };
Auto_ptr<Payload> ap1{ new Payload{11} };
/* Move assignment operation */
ap1 = ap0;
/* To check they still exist */
std::cerr << ap0.getId() << ' ' << ap1.getId() << '\n';
/* To check payload still exists */
std::cerr << "Payload " << ap1->getId() << " has value " << ap1-
>getVal()
<< '\n';
return 0;
}

nascardriver
May 20, 2019 at 5:21 am · Reply

> The copy constructor is now a move constructor


It's still a copy constructor, move constructors look different, they're
https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 25/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
covered later. But it behaves like a move constructor.

> I still don't understand this


@operator-> is special. C++ knows that it returns a pointer or reference to an object,
and it will perform the member lookup for you. This isn't something you can do with
regular functions or other operators.

Everything else is correct :)

Anthony
May 20, 2019 at 9:13 am · Reply

You are one helpful dude, nascardriver :)

Michael Stef
April 4, 2019 at 8:52 am · Reply

i don't understand how can "ptr->sayHi()" works with overloaded -> operator, it seems like it must be
used twice so ptr-> first resolves and return address of res and then another arrow operator must be
used res->sayHi() ?

nascardriver
April 4, 2019 at 9:57 am · Reply

Hi Michael!

Good point.
What you said is correct if we take the long way of calling @operator->

1 Resource *pResource{ ptr.operator->() };


2 pResource->sayHi();
3
4 // or
5
6 ptr.operator->()->sayHi();

But if we use @operator-> directly, we don't need another dereference or arrow.

Anthony
May 19, 2019 at 11:00 am · Reply

I have exactly this problem. I don't get it :( Like Michael Stef says, it seems we must
surely use the -> operator twice.

When we overload the -> operator as described in this chapter:

1 T* operator->() const {
2 return m_ptr;

the expression resolves to (or returns, or is replaced by) the pointer. Then, to access any member of
the Resource object, we should have to use this pointer with the -> operator in the normal manner. But
now we're using the -> twice, which is not correct..

Agh!

Yogesh
March 18, 2019 at 4:32 am · Reply

I am trying to delete the ptr before assigning to new value in copy constructor(like assignment
operator) , but doing that ,i am getting error.

https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 26/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
Could you please help me to root cause the error .

Alex
March 19, 2019 at 8:04 pm · Reply

Can you post your code? There isn't enough information here to determine what you might be
doing incorrectly.

Yogesh
March 20, 2019 at 12:14 am · Reply

template<typename T>
class AutoPtr
{
T *ptr;
public:
AutoPtr(T *p=nullptr):ptr(p)
{}
~AutoPtr()
{
delete ptr;
}
T& operator *()
{
return *ptr;
}
T* operator->()
{
return ptr;
}
AutoPtr( AutoPtr& source)
{
delete ptr;
ptr = source.ptr;
source.ptr = NULL;
}
AutoPtr& operator =(AutoPtr& source)
{
delete ptr;
ptr = source.ptr;
source.ptr = NULL;
return *this;
}
};

class Resource
{
public:
Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource destroyed\n"; }
};

int main()
{
AutoPtr<Resource> temp = new Resource();
AutoPtr<Resource> temp1 = temp;
getchar();
getchar();
}
https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 27/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++

Yogesh
March 20, 2019 at 12:17 am · Reply

Hi Alex ,
I posted the code .
While calling the copy constructor , there was no existing value of ptr . But i am trying to delete that,
which should not cause any problem (because compiler checks the pointer before deleting ), but i am
getting run-time exception .

nascardriver
March 20, 2019 at 4:19 am · Reply

Enable compiler warnings, read them, fix them. Lesson 0.10, 0.11.
@AutoPtr::ptr is uninitialized in @AutoPtr::AutoPtr(AutoPtr &).

Danty Wong
March 7, 2019 at 8:05 pm · Reply

Hi,

1 int main()
2 {
3 Auto_ptr2<Resource> res1(new Resource);
4 Auto_ptr2<Resource> res2; // Start as nullptr
5
6 std::cout << "res1 is " << (res1.isNull() ? "null\n" : "not null\n");
7 std::cout << "res2 is " << (res2.isNull() ? "null\n" : "not null\n");
8
9 res2 = res1; // res2 assumes ownership, res1 is set to null
10
11 std::cout << "Ownership transferred\n";
12
13 std::cout << "res1 is " << (res1.isNull() ? "null\n" : "not null\n");
14 std::cout << "res2 is " << (res2.isNull() ? "null\n" : "not null\n");
15
16 return 0;
17 }

When res2 is declared, only a chunk of memory large enough for a Resource class object is set aside, so no
constructor is called for creating res2.
In the middle of the programme, the content of res1's m_ptr field is 'moved' to res2's m_ptr field. At what point does
res2 gets its m_ptr field declared?

At the end of the programme, only one line of "Resource destroyed" is printed. Is this from the destructor of res1 or
res2? And if only one of the destructor is called, what about the other object in the block? Does it get destroyed as
well?

Thanks

Alex
March 8, 2019 at 4:37 pm · Reply

> When res2 is declared, only a chunk of memory large enough for a Resource class object is
set aside, so no constructor is called for creating res2.

No, when res2 is declared, an Auto_ptr2 is allocated, which contains a null pointer (m_ptr). No memory is
set aside for a Resource object.

When the move happens, the res1's m_ptr is transfered to res2's m_ptr. At this point, res1's m_ptr is set to
null, as it no longer owns the object.

https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 28/29
05/05/2021 M.1 — Intro to smart pointers and move semantics | Learn C++
At the end, the destructor is called from res2, as res2 is the owner of the resource.

There is no other object in the block -- there's only one Resource (destroyed by res2) and two Auto_ptr2
that are automatically deallocated when the function ends.

« Older Comments 1 2

https://www.learncpp.com/cpp-tutorial/intro-to-smart-pointers-move-semantics/ 29/29

You might also like