adding lecture 08
This commit is contained in:
205
lectures/08_vector_implementation/README.md
Normal file
205
lectures/08_vector_implementation/README.md
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
# Lecture 8 --- Templated Classes & Vector Implementation
|
||||||
|
|
||||||
|
Designing our own container classes:
|
||||||
|
– Mimic the interface of standard library (STL) containers
|
||||||
|
– Study the design of memory management.
|
||||||
|
– Move toward eventually designing our own, more sophisticated classes.
|
||||||
|
- Vector implementation
|
||||||
|
- Templated classes (including compilation and instantiation of templated classes)
|
||||||
|
- Copy constructors, assignment operators, and destructors
|
||||||
|
|
||||||
|
## 8.1 Vector Public Interface
|
||||||
|
|
||||||
|
In creating our own version of the STL vector class, we will start by considering the public interface:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
public:
|
||||||
|
// MEMBER FUNCTIONS AND OTHER OPERATORS
|
||||||
|
T& operator[] (size_type i);
|
||||||
|
const T& operator[] (size_type i) const;
|
||||||
|
void push_back(const T& t);
|
||||||
|
void resize(size_type n, const T& fill_in_value = T());
|
||||||
|
void clear();
|
||||||
|
bool empty() const;
|
||||||
|
size_type size() const;
|
||||||
|
```
|
||||||
|
|
||||||
|
- To implement our own generic (a.k.a. templated) vector class, we will implement all of these operations,
|
||||||
|
manipulate the underlying representation, and discuss memory management.
|
||||||
|
|
||||||
|
## 8.2 Templated Class Declarations and Member Function Definitions
|
||||||
|
|
||||||
|
- In terms of the layout of the code in vec.h (pages 5 & 6 of the handout), the biggest difference is that this is a
|
||||||
|
templated class. The keyword template and the template type name must appear before the class declaration:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
template <class T> class Vec
|
||||||
|
```
|
||||||
|
|
||||||
|
- Within the class declaration, T is used as a type and all member functions are said to be “templated over type
|
||||||
|
T”. In the actual text of the code files, templated member functions are often defined (written) inside the class
|
||||||
|
declaration.
|
||||||
|
|
||||||
|
- The templated functions defined outside the template class declaration must be preceded by the phrase:
|
||||||
|
template <class T> and then when Vec is referred to it must be as Vec<T> . For example, for member
|
||||||
|
function create (two versions), we write:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
template <class T> void Vec<T>::create(...
|
||||||
|
```
|
||||||
|
|
||||||
|
## 8.3 Syntax and Compilation
|
||||||
|
|
||||||
|
- Templated classes and templated member functions are not created/compiled/instantiated until they are
|
||||||
|
needed. Compilation of the class declaration is triggered by a line of the form: Vec<int> v1; with int
|
||||||
|
replacing T. This also compiles the default constructor for Vec<int> because it is used here. Other member
|
||||||
|
functions are not compiled unless they are used.
|
||||||
|
- When a different type is used with Vec, for example in the declaration: Vec<double> z; the template class
|
||||||
|
declaration is compiled again, this time with double replacing T instead of int. Again, however, only the
|
||||||
|
member functions used are compiled.
|
||||||
|
- This is very different from ordinary classes, which are usually compiled separately and all functions are compiled
|
||||||
|
regardless of whether or not they are needed.
|
||||||
|
- The templated class declaration and the code for all used member functions must be provided where they
|
||||||
|
are used. As a result, member functions definitions are often included within the class declaration or defined
|
||||||
|
outside of the class declaration but still in the .h file. If member function definitions are placed in a separate
|
||||||
|
.cpp file, this file must be #include-d, just like the .h file, because the compiler needs to see it in order to
|
||||||
|
generate code. (Normally we don’t #include .cpp files!) See also diagram on page 7 of this handout.
|
||||||
|
Note: Including function definitions in the .h for ordinary non-templated classes may lead to compilation errors
|
||||||
|
about functions being “multiply defined”. Some of you have already seen these errors.
|
||||||
|
|
||||||
|
## 8.4 Member Variables
|
||||||
|
|
||||||
|
Now, looking inside the Vec<T> class at the member variables:
|
||||||
|
- m_data is a pointer to the start of the array (after it has been allocated). Recall the close relationship between
|
||||||
|
pointers and arrays.
|
||||||
|
- m_size indicates the number of locations currently in use in the vector. This is exactly what the size()
|
||||||
|
member function should return,
|
||||||
|
- m_alloc is the total number of slots in the dynamically allocated block of memory.
|
||||||
|
Drawing pictures, which we will do in class, will help clarify this, especially the distinction between m size and
|
||||||
|
m alloc.
|
||||||
|
|
||||||
|
## 8.5 Typedefs
|
||||||
|
|
||||||
|
- Several types are created through typedef statements in the first public area of Vec. Once created the names
|
||||||
|
are used as ordinary type names. For example Vec<int>::size type is the return type of the size() function,
|
||||||
|
defined here as an unsigned int.
|
||||||
|
|
||||||
|
## 8.6 operator[]
|
||||||
|
|
||||||
|
Access to the individual locations of a Vec is provided through operator[]. Syntactically, use of this operator
|
||||||
|
is translated by the compiler into a call to a function called operator[]. For example, if v is a Vec<int>,
|
||||||
|
then:
|
||||||
|
```cpp
|
||||||
|
v[i] = 5;
|
||||||
|
```
|
||||||
|
translates into:
|
||||||
|
```cpp
|
||||||
|
v.operator[](i) = 5;
|
||||||
|
```
|
||||||
|
<EFBFBD> In most classes there are two versions of operator[]:
|
||||||
|
– A non-const version returns a reference to m data[i]. This is applied to non-const Vec objects.
|
||||||
|
– A const version is the one called for const Vec objects. This also returns m data[i], but as a const
|
||||||
|
reference, so it can not be modified.
|
||||||
|
|
||||||
|
## 8.7 Default Versions of Assignment Operator and Copy Constructor Are Dangerous!
|
||||||
|
|
||||||
|
Before we write the copy constructor and the assignment operator, we consider what would happen if we didn’t
|
||||||
|
write them.
|
||||||
|
<EFBFBD> C++ compilers provide default versions of these if they are not provided. These defaults just copy the values
|
||||||
|
of the member variables, one-by-one. For example, the default copy constructor would look like this:
|
||||||
|
```cpp
|
||||||
|
template <class T>
|
||||||
|
Vec<T> :: Vec(const Vec<T>& v)
|
||||||
|
: m_data(v.m_data), m_size(v.m_size), m_alloc(v.m_alloc)
|
||||||
|
{}
|
||||||
|
```
|
||||||
|
In other words, it would construct each member variable from the corresponding member variable of v. This is
|
||||||
|
dangerous and incorrect behavior for the Vec class. We don’t want to just copy the m_data pointer. We really
|
||||||
|
want to create a copy of the entire array! Let’s look at this more closely...
|
||||||
|
|
||||||
|
## 8.8 Exercise
|
||||||
|
|
||||||
|
Suppose we used the default version of the assignment operator and copy constructor in our Vec<T> class. What
|
||||||
|
would be the output of the following program? Assume all of the operations except the copy constructor behave as
|
||||||
|
they would with a std::vector<double>.
|
||||||
|
```cpp
|
||||||
|
Vec<double> v(4, 0.0);
|
||||||
|
v[0] = 13.1; v[2] = 3.14;
|
||||||
|
Vec<double> u(v);
|
||||||
|
u[2] = 6.5;
|
||||||
|
u[3] = -4.8;
|
||||||
|
for (unsigned int i=0; i<4; ++i)
|
||||||
|
cout << u[i] << " " << v[i] << endl;
|
||||||
|
```
|
||||||
|
Explain what happens by drawing a picture
|
||||||
|
of the memory of both u and v.
|
||||||
|
|
||||||
|
## 8.9 Classes With Dynamically Allocated Memory
|
||||||
|
|
||||||
|
For Vec (and other classes with dynamically-allocated memory) to work correctly, each object must do its own
|
||||||
|
dynamic memory allocation and deallocation. We must be careful to keep the memory of each object instance
|
||||||
|
separate from all others.
|
||||||
|
<EFBFBD> All dynamically-allocated memory for an object should be released when the object is finished with it or when
|
||||||
|
the object itself goes out of scope (through what’s called a destructor).
|
||||||
|
<EFBFBD> To prevent the creation and use of default versions of these operations, we must write our own:
|
||||||
|
– Copy constructor
|
||||||
|
– Assignment operator
|
||||||
|
– Destructor
|
||||||
|
|
||||||
|
## 8.10 The “this” pointer
|
||||||
|
|
||||||
|
All class objects have a special pointer defined called this which simply points to the current class object, and
|
||||||
|
it may not be changed.
|
||||||
|
- The expression \*this is a reference to the class object.
|
||||||
|
- The this pointer is used in several ways:
|
||||||
|
- Make it clear when member variables of the current object are being used.
|
||||||
|
- Check to see when an assignment is self-referencing.
|
||||||
|
- Return a reference to the current object.
|
||||||
|
|
||||||
|
## 8.11 Copy Constructor
|
||||||
|
|
||||||
|
This constructor must dynamically allocate any memory needed for the object being constructed, copy the
|
||||||
|
contents of the memory of the passed object to this new memory, and set the values of the various member
|
||||||
|
variables appropriately.
|
||||||
|
- Exercise: In our Vec class, the actual copying is done in a private member function called copy. Write the
|
||||||
|
private member function copy.
|
||||||
|
|
||||||
|
## 8.12 Assignment Operator
|
||||||
|
|
||||||
|
Assignment operators of the form: v1 = v2;
|
||||||
|
are translated by the compiler as: v1.operator=(v2);
|
||||||
|
- Cascaded assignment operators of the form: v1 = v2 = v3;
|
||||||
|
are translated by the compiler as: v1.operator=(v2.operator=(v3));
|
||||||
|
- Therefore, the value of the assignment operator (v2 = v3) must be suitable for input to a second assignment
|
||||||
|
operator. This in turn means the result of an assignment operator ought to be a reference to an object.
|
||||||
|
- The implementation of an assignment operator usually takes on the same form for every class:
|
||||||
|
– Do no real work if there is a self-assignment.
|
||||||
|
– Otherwise, destroy the contents of the current object then copy the passed object, just as done by the
|
||||||
|
copy constructor. In fact, it often makes sense to write a private helper function used by both the copy
|
||||||
|
constructor and the assignment operator.
|
||||||
|
– Return a reference to the (copied) current object, using the this pointer.
|
||||||
|
|
||||||
|
## 8.13 Destructor (the “constructor with a tilde/twiddle”)
|
||||||
|
|
||||||
|
The destructor is called implicitly when an automatically-allocated object goes out of scope or a dynamicallyallocated object is deleted. It can never be called explicitly!
|
||||||
|
- The destructor is responsible for deleting the dynamic memory “owned” by the class.
|
||||||
|
- The syntax of the function definition is a bit weird. The ~ has been used as a bit-wise inverse or logic negation
|
||||||
|
in other contexts.
|
||||||
|
|
||||||
|
## 8.14 Increasing the Size of the Vec
|
||||||
|
|
||||||
|
- push_back(T const& x) adds to the end of the array, increasing m size by one T location. But what if the
|
||||||
|
allocated array is full (m size == m alloc)?
|
||||||
|
1. Allocate a new, larger array. The best strategy is generally to double the size of the current array. Why?
|
||||||
|
2. If the array size was originally 0, doubling does nothing. We must be sure that the resulting size is at
|
||||||
|
least 1.
|
||||||
|
3. Then we need to copy the contents of the current array.
|
||||||
|
4. Finally, we must delete current array, make the m data pointer point to the start of the new array, and
|
||||||
|
adjust the m size and m alloc variables appropriately.
|
||||||
|
- Only when we are sure there is enough room in the array should we actually add the new object to the back
|
||||||
|
of the array
|
||||||
|
|
||||||
|
## 8.15 Exercises
|
||||||
|
|
||||||
|
Finish the definition of Vec::push_back.
|
||||||
|
- Write the Vec::resize function.
|
||||||
Reference in New Issue
Block a user