1. CS225
    1. Introduction
    2. Lesson 1
    3. Lesson 2
    4. Lesson 3
    5. Lesson 4
  2. CS251
    1. The Beginning
  3. CS180
    1. Introduction
    2. Midterm Revision
    3. Finals Revision
    4. CS251
    5. Memory
    6. ELF Format
    7. History of Computers
    8. Stuff to Research
    9. Quiz To-Do
    10. OS Architectures
    11. Week 3
    12. Week 4
    13. Week 5
    14. Week 6 (Threads)
    15. Week 7 (Scheduling)
    16. Week 8 (Thread Scheduling)
    17. Week 9 (Memory Management)


    1. OOP
      1. Inheritance
      2. R-Value References
    2. Misc Notes
    3. Size/Offsets of Structs/Classes
    4. Week 4
    1. Introduction
    2. Scan Conversion
    3. Quiz 01
    1. Notes

Lesson 4

*rhs.pi is *(rhs.pi) - read it right then left, similar to complex declarations.

Conversion Constructors

C(int s);

Automatically Generated Constructors

Compiler-generated constructors will be more efficient than user-generated because compilers are allowed to cheat.

Compiler generates garbage for constructors

Rule: If there are in-class variables make sure you initialize them inside the constructor.

Copy Constructors - User and Compiler Generated

A copy constructor is automatically generated. It copies variables and it copies arrays (so, arrays are not shared between the two objects). However, do realize it copies pointers as well, which means that dereferencing a copied pointer will cause the same memory to be modified.

An automatically generated copy constructor performs a shallow copy.

Let's sidetrack for a bit: What is the size of an std::vector<int> v that has a million ints? Hint: It ain't 4 million. It is around 24 bytes (in most standard implementations).
From StackOverflow:
The 24 size you see can be explained as 3 pointers (each pointer is 8 bytes in size on 64-bit architectures; so you have 3 x 8 = 24 bytes). These pointers can be:
• begin of vector
• end of vector
• end of reserved memory for vector (i.e. vector's capacity)

So, normally we want a user-generated copy constructor. Remember that it's all or nothing - it's either your copy constructor or the compiler's. If you just initialize a few variables, the rest will probably be garbage.

Another problem with automatic copy constructors is double deletes - if two objects share the same pointer to some memory and delete them in the destructor, it will cause a double delete.

Destructors - User and Compiler Generated

Compiler-generated destructors does not do anything. We just say it's there because there has to be a destructor. The compiler makes a promise that there will be a constructor constructing the object and a destructor destructing the object. Think about it - how do we know about the lifetime of the object without this promise?

Calling a destructor is not a good idea unless you know exactly what you are doing.

int main() {
C c;
//c gets deleted here AGAIN. Double delete!

It is useful to think of it this way - most of the stuff (pointers, arrays, vars) are compiler “controlled”. However, let's say you have a pointer pointing to some memory. That will be client “controllled”, so you have to make sure to destroy that on your user-generated destructor.


For automatic destructors, lifetimes of objects are by order of constructor of each object. This is only true if these objects are on the stack. For dynamically allocated content, the user is in charge of the lifetime of each object.

C c;
D d;//D will be alive as long as c is alive.

Implicit vs Explicit Constructors

C c; //Implicit
C(2); //Explicit, does not have a name, temporary, compiler is allowed to optimize it much more aggressively than a named object. As efficient as possible.

Sometimes it is desirable NOT to have a particular constructor (for example, if you don't want users to copy something, or, say, the Singleton pattern.)

private//This is required
C (C const&); //declared, but never defined. Using it will result in an error.

public//Can be in public
C (C const&) = delete//You will never be able to use this function.
~C() = delete//legal, but you would not be able to use this class. So don't do this.

“this” pointer

Useful to differentiate between local variables and parameters to data members. (this->whatever).

It is also useful to refer to the whole current object (return *this).
It is also useful to refer to the whole current object (return *this).

Remember, this is a value type of "C * const this".

foo(whatever) const will cause the this pointer to be “C const * const this”. So you can't modify C now.

A note on const refs

If you have the option to return by const ref (C const &) instead of return by val, do it.

Assignment Operator

Part of the big 5 - Default Copy, Constructor, Assignment, Destructor, Stream Insertion Operator (std::ostream::operator<<) (for debugging) (

C& operator=(const C& rhs);

void operator=(const C& rhs); //this is legal, but prevents chain assignments

operator=(const C& rhs) //this is legal, but no. Don't. WHY. Well, it may be useful. Just know what you're doing.

- Argument is usually a const ref, but doesnt NEED to be
- returns a reference
- will be generated by compiler if not user defined
- Shallow (this should be obvious), so there can be double frees if you aren't careful.

Difference between copy constructor and assignment is copy constructor works with new + old, assignment works with old+old. If you use assignment willy-nillly you can get a leak. Let's say you have a pointer to memory you haven't freed up yet and decide to assign shit. Then you're up shit creek with no paddle.

if(this!=&rhs) //Are these the same object?
if(*this!=rhs) //Do these two objects LOOK the same? (Useful to use to see if there is a reason for assigning)

Since assignments are so similar to copy constructors, can we share code? For the most part, no:

C (C const& rhs) {*this = rhs;} //don't do this - remember, the compiler still makes a member initializer list even if it's just garbage. Just utilizing an MIL would be much more efficient.

C& operator=(C const& rhs) { return C(rhs); } //doesn't make sense. Returning a new temporary thing instead of the current thing.

C& operator=(C const & rhs) { *this = C(rhs); return *this; } //infinite recursive call. runs forever. inception bullshit.

If you really want to, create another function and call them both from the copy constructor and assignment operator. (Though, be aware of the Member Initialization List.)


This is very useful for almost all classes. It is relatively simple. Most of the time, you just swap pointers, like this:



C& operator=(C rhs) { swap(rhs); return *this; } //This may seem to go out of scope since we're using a no-name temporary - however, do remember that we're swapping out the pointers - what will stay is the "temporary" which is now "*this", and what will die is the old "*this".