Today's lecture and studio focus on copy and move semantics, addressing issues
that may arise when objects of a class act mostly like values (for which copy
semantics is the main issue) versus those of another class that may act mostly
like pointers (for which copy or move semantics may be relevant). We will discuss
how to ask the compiler explicitly to synthesize member functions using
= default
and also how to prevent them from being synthesized using
= delete
. We will also cover lvalues and rvalues,
and lvalue references and rvalue references, which establish foundations for move
semantics (in which an object's implementation is stolen rather than copied).
Please complete the following required exercises. I encourage you to please work in groups of 2 or 3 people on each studio (and the groups are allowed to change from studio to studio) though if you would prefer to complete any studio by yourself that is also allowed.
As you work through these exercises, please record your answers, and when
you finish them please log into Canvas, select this course in this semester, and
then upload a .txt
file containing your answers on the Canvas page
for this studio assignment. Only one submission per team, please, and if
you need to re-submit it the person who originally submitted the studio should
please be the one to do that.
Make sure that the name of each person who worked on these exercises is listed in the first answer, and make sure you number each of your responses so it is easy to match your responses with each exercise.
First, ssh into shell.cec.wustl.edu
using your WUSTL
Key id and password and then use qlogin
to log into one
of the Linux Lab machines and then confirm that the version of g++
there is correct (as you did in Studio 0). Then
cd
into your directory for this course this semester, create a new
subdirectory for this studio, and cd
into it.
Copy the Makefile
from one of your previous studios into that
directory, and add a source file for your program's main function. As you
work on this studio, please update the Makefile as needed so that it will build an
executable program called studio11
from the files in that directory.
Add a header file to your directory, and in it declare a class that at first
only has a private member variable of type string
.
In your program's main function, declare an object of that class type on the stack, copy construct another object of that type from it, and then return a descriptively named symbol whose value is 0 to indicate success.
Build and run your program, and as the answer to this exercise please show your declaration of the class.
Declare and (in a source file you should please add to your directory for this studio) define a public copy constructor for your class, which initializes its private member variable using the private member variable of the object from which it is being copy constructed, in its base/member initialization list. In the body of the copy constructor, print out the addresses of the object that is being constructed and of the object from which it is being constructed.
Try to build your program, which should fail because the presence of the
copy constructor causes the compiler not to synthesize a default constructor
automatically. Declare (but do not define) a default constructor for your
class using = default
to ask the compiler to synthesize one for you.
Compile and run your program, and as the answer to this exercise please show the output it produced.
Modify the body of your copy constructor so that it in addition to the addresses of the objects involved it also prints out the private member variable of the object that is being constructed.
Declare and define a constructor that takes in a const reference to a
string
and uses that to initialize the private member variable
in the base/member initialization list. In the body of that constructor please
have it print out the address and the private member variable of the object
being constructed.
Also declare and define a destructor that prints out the address and the private member variable of the object being destroyed.
In your main function, replace the line that default constructs an object
of the class type with one that initializes it with a C-style string (e.g.,
"hello"
).
Compile and run your program, and as the answer to this exercise please show the output it produced.
Declare and define a public assignment operator for your class, which takes a const reference to an object of the class type and returns a non-const reference to an object of the class type. That operator should (1) first print out the address and private member variable of the object on which it is being called, followed by the address and private member variable of the object that was passed into it; (2) assign its private member variable the private member variable of the object that was passed into it; and then (3) return a (non-const) reference to the object on which it was invoked.
In your main function, use different C-style strings to construct at least three
different objects and then in a single statement use the assignment operator
twice to assign the first object to the second object and the second object to
the third one (e.g., c = b = a;
.
Compile and run your program, and as the answer to this exercise please show the output it produced.
Remove the contents of your main function, except for the statement at the end that returns a value indicating success.
In your main function, declare a unique_ptr
that is parameterized
with your class type, and initialize it with a call to new
that
dynamically constructs an object of your class type from a C-style string,
and then declare another unique_ptr
that is parameterized with your
class type, and initialize it with the first unique_ptr
.
Try to build your program (which should fail since the copy constructor for
unique_ptr
is deleted). Then, modify the initialization of the
second unique_ptr
so that it wraps the other unique_ptr
with a call to std::move
.
Build and run your program, and as the answer to this exercise, please show the output that was produced.
In the header and source files for your class, declare and define a public virtual member function that prints to the standard output stream the object's address and its private member variable. Also modify the declaration of the destructor so that it is virtual as well.
In the source file where you defined your program's main function, define a
function with a void
return type that takes a unique_ptr
to your class type by value, and uses it to invoke the public
virtual member function of the object to which the unique_ptr
points.
In your program's main function, pass the second unique_ptr
(not the first one, which is no longer safe to use because of your use of
std::move
to transfer ownership of the object of your class type
to the second unique_ptr
) into a call to the function you just
defined.
Try to build your program (which again should fail since the copy constructor for
unique_ptr
is deleted). Then, modify the call to the function so that
it wraps the second unique_ptr
with a call to std::move
.
Build and run your program, and as the answer to this exercise, please show the output that was produced.
In the source file where you defined your program's main function, change the
function you defined in the previous exercise so that it returns by
value the unique_ptr
that is passed into it.
In your main function, assign the value that is returned by that function
to the first unique_ptr
, and then use the first
unique_ptr
to invoke the virtual public member
function of the object that it points to.
Build and run your program, and as the answer to this exercise, please show the output that the program produced.
For this studio, please turn in the following:
Page posted Tuesday October 4, 2022, by Chris Gill.