What is a copy constructor? When is it used?
Explain why the following declaration is illegal:
Sales_data::Sales_data(Sales_data rhs);
What happens when we copy a StrBlob? What about StrBlobPtrs?
Assuming Point is a class type with a public copy constructor, iden- tify each use of the copy constructor in this program fragment:
Point global;
Point foo_bar(Point arg)
{
Point local = arg, *heap = new Point(global);
*heap = local;
Point pa[ 4 ] = { local, *heap };
return *heap;
}
Given the following sketch of a class, write a copy constructor that copies all the members. Your constructor should dynamically allocate a new string (§ 12.1.2, p. 458) and copy the object to which ps points, rather than copying ps itself.
class HasPtr {
public:
HasPtr(const std::string &s = std::string()): ps(new std::string(s)), i(0) { }
private:
std::string *ps;
int i;
};
What is a copy-assignment operator? When is this operator used? What does the synthesized copy-assignment operator do? When is it synthesized?
What happens when we assign one StrBlob to another? What about StrBlobPtrs?
Write the assignment operator for the HasPtr class from exercise 13.5 in § 13.1.1 (p. 499). As with the copy constructor, your assignment operator should copy the object to which ps points.
What is a destructor? What does the synthesized destructor do? When is a destructor synthesized?
What happens when a StrBlob object is destroyed? What about a StrBlobPtr?
Add a destructor to your HasPtr class from the previous exercises.
How many destructor calls occur in the following code fragment?
bool fcn(const Sales_data *trans, Sales_data accum)
{
Sales_data item1(*trans), item2(accum);
return item1.isbn() != item2.isbn();
}
A good way to understand copy-control members and constructors is to define a simple class with these members in which each member prints its name:
struct X {
X() {std::cout << "X()" << std::endl;}
X(const X&) {std::cout << "X(const X&)" << std::endl;}
};
Add the copy-assignment operator and destructor to X and write a program using X objects in various ways: Pass them as nonreference and reference parameters; dynam- ically allocate them; put them in containers; and so forth. Study the output until you are certain you understand when and why each copy-control member is used. As you read the output, remember that the compiler can omit calls to the copy constructor.
Assume that numbered is a class with a default constructor that gen- erates a unique serial number for each object, which is stored in a data member named mysn. Assuming numbered uses the synthesized copy-control members and given the following function:
void f (numbered s) { cout << s.mysn << endl; }
what output does the following code produce?
numbered a, b = a, c = b;
f(a); f(b); f(c);
Assume numbered has a copy constructor that generates a new serial number. Does that change the output of the calls in the previous exercise? If so, why? What output gets generated?
What if the parameter in f were const numbered&? Does that change the output? If so, why? What output gets generated?
Write versions of numbered and f corresponding to the previous three exercises and check whether you correctly predicted the output.
Define an Employee class that contains an employee name and a unique employee identifier. Give the class a default constructor and a constructor that takes a string representing the employee’s name. Each constructor should generate a unique ID by incrementing a static data member.
Does your Employee class need to define its own versions of the copy-control members? If so, why? If not, why not? Implement whatever copy-control members you think Employee needs.
Explain what happens when we copy, assign, or destroy objects of our TextQuery and QueryResult classes from § 12.3 (p. 484).
Do you think the TextQuery and QueryResult classes need to de- fine their own versions of the copy-control members? If so, why? If not, why not? Implement whichever copy-control operations you think these classes require.
Assume that we want HasPtr to behave like a value. That is, each object should have its own copy of the string to which the objects point. We’ll show the definitions of the copy-control members in the next section. However, you already know everything you need to know to implement these members. Write the HasPtr copy constructor and copy-assignment operator before reading on.
Compare the copy-control members that you wrote for the solutions to the previous section’s exercises to the code presented here. Be sure you understand the differences, if any, between your code and ours.
What would happen if the version of HasPtr in this section didn’t define a destructor? What if HasPtr didn’t define the copy constructor?
Assume we want to define a version of StrBlob that acts like a value. Also assume that we want to continue to use a shared_ptr so that our StrBlobPtr class can still use a weak_ptr to the vector. Your revised class will need a copy constructor and copy-assignment operator but will not need a destructor. Explain what the copy constructor and copy-assignment operators must do. Explain why the class does not need a destructor.
Write your own version of the StrBlob class described in the previous exercise.
Define your own reference-counted version of HasPtr.
Given the following classes, implement a default constructor and the necessary copy-control members.
(a) class TreeNode {
private:
std::string value;
int count;
TreeNode *left;
TreeNode *right;
};
(b) class BinStrTree {
private:
TreeNode *root;
};
Explain why the calls to swap inside swap(HasPtr&, HasPtr&) do not cause a recursion loop.
Write and test a swap function for your valuelike version of HasPtr. Give your swap a print statement that notes when it is executed.
Give your class a < operator and define a vector of HasPtrs. Give that vector some elements and then sort the vector. Note when swap is called.
Would the pointerlike version of HasPtr benefit from defining a swap function? If so, what is the benefit? If not, why not?
Why is the parameter to the save and remove members of Message a Folder&? Why didn’t we define that parameter as Folder? Or const Folder&?
Write the Message class as described in this section.
What would happen if Message used the synthesized versions of the copy-control members?
Design and implement the corresponding Folder class. That class should hold a set that points to the Messages in that Folder.
Add members to the Message class to insert or remove a given Folder* into folders. These members are analogous to Folder’s addMsg and remMsg operations.
We did not use copy and swap to define the Message assignment operator. Why do you suppose this is so?
Write your own version of StrVec, including versions of reserve, capacity (§ 9.4, p. 356), and resize (§ 9.3.5, p. 352).
Add a constructor that takes an initializer_list to your StrVec class.
Why did we use postfix increment in the call to construct inside push_back? What would happen if it used the prefix increment?
Test your StrVec class by using it in place of the vector in your TextQuery and QueryResult classes (§ 12.3, p. 484).
Rewrite the free member to use for_each and a lambda (§ 10.3.2, p. 388) in place of the for loop to destroy the elements. Which implementation do you prefer, and why?
Write a class named String that is a simplified version of the library string class. Your class should have at least a default constructor and a constructor that takes a pointer to a C-style string. Use an allocator to allocate memory that your String class uses.
Distinguish between an rvalue reference and an lvalue reference.
Which kind of reference can be bound to the following initializers?
int f();
vector<int> vi(100);
int? r1 = f();
int? r2 = vi[0];
int? r3 = r1;
int? r4 = vi[0] * f();
Give the copy constructor and copy-assignment operator in your String class from exercise 13.44 in § 13.5 (p. 531) a statement that prints a message each time the function is executed.
Define a vector and call push_back several times on that vector. Run your program and see how often Strings are copied.
Add a move constructor and move-assignment operator to your StrVec, String, and Message classes.
Put print statements in the move operations in your String class and rerun the program from exercise 13.48 in § 13.6.1 (p. 534) that used a vector to see when the copies are avoided.
Although unique_ptrs cannot be copied, in § 12.1.5 (p. 471) we wrote a clone function that returned a unique_ptr by value. Explain why that func- tion is legal and how it works.
Explain in detail what happens in the assignments of the HasPtr ob- jects on page 541. In particular, describe step by step what happens to values of hp, hp2, and of the rhs parameter in the HasPtr assignment operator.
As a matter of low-level efficiency, the HasPtr assignment operator is not ideal. Explain why. Implement a copy-assignment and move-assignment oper- ator for HasPtr and compare the operations executed in your new move-assignment operator versus the copy-and-swap version.
What would happen if we defined a HasPtr move-assignment oper- ator but did not change the copy-and-swap operator? Write code to test your answer.
Add an rvalue reference version of push_back to your StrBlob.
What would happen if we defined sorted as:
Foo Foo::sorted() const & {
Foo ret(*this);
return ret.sorted();
}
What if we defined sorted as:
Foo Foo::sorted() const & { return Foo(*this).sorted(); }
Write versions of class Foo with print statements in their sorted functions to test your answers to the previous two exercises.