Object Oriented Programming
Fundamentals
.h vs .cpp
- .h defines public interface API
- .cpp handle all the dirty details
why so may extensions?
header file: .h, .hh, .hpp
source file: .cc, .cpp, .cxx, .c++, .C
Depends on the compiler
Const
Why use CONST?
There is no excuse for ignoring the safety mechanisms provided with a product, and there is particularly no excuse for programmers too lazy to write const-correct code.
same as Why don’t we use global variables?
- “Non-const variables can be read or modified by any part of the function“ <**NOT SAFE!!!**>
- if we don’t use const, we can’t spot the bug easily in the above code
- if we use const instead of the one above:
- we could get errors from the compiler(compiler time error):
- Besides, const allows us to reason about whether a variable will be changed
const and Classes
- can’t call non-const member function on const object:
Const Iterators
const vector
::iterator itr acts like int* const itr to make an iterator read-only, define a new const_iterator
Operators
Operator Overloading
When dealing with user-defined class:
1 | // member |
1 | // const member |
So, implement as Member of Non_Member?
- [], (), ->, = must be implemented as member
- <<, implemented as non-member
- +,< if binary op treats both operands equally, implement as non-member
- += if binary op doesn’t treat both operands equally( change lhs)
1 | // non-member function |
POLA(Principle of Least Astonishment)
design operators primarily to mimic conventional usage
if operator is ambiguous , don’t overload that operator
use non-member functions for symmetric operators
The second one doesn’t work if overloaded as member function.
- always provide all out of a set of related operators
don’t astonish others: if you implement < , you should also implement > etc.
- always think about const vs non-const for member functions:
Functors (basically lambdas)
Advanced Multi-threading Support (C++20)
1 | awaiter operator co_await() const noexcept { |
Special Member Functions
1 | StringVector vec7 = vec4;// copy constructor |
when we don’t use default copy constructor?
when we have ownership issues:
- ownership issues: a member is a handle on a resource outside of the class
- pointers
- mutexes
- filestreams
Rule of Three
- if you explicitly define a copy constructor, copy assignment of destructor, you should define all three
when we need to declare a destructor, we have to take care of the ownership issues, the issues happen to other two.
Move Semantics
emplace_back?
- based on the philosophy: if you don’t need any copy, don’t do it
- for larger objects
All things have its beginning
What happens when running the following codes?
1 | StrVector readNames(size_t size){ |
- you may find it surprising, lots of copy constructor and destructor!
so any optimization?
copy elision, c++17, compiler make the copy only once in the caller function(main)
still have any further optimization ?
YES!! The cool thing move can save us more time.
in the following code:
1 | name2 = readNames(54321234); |
- readNames is a temporary value, and will become homeless and will be destroyed, why we need to copy it? There is no need!
- We could move(steal) the resources instead!
lvalue vs. rvalue
- lvalue: has a name, can find address
- rvalue: no name, temporary
1 | v1[1] = 4*i; // v1[1] is a reference to named value "lvalue" |
lvalue reference vs. rvalue reference
1 | auto&& v4 = v1+v2; // save the rvalue, or expands its life time! |
- any changes to v4 will change (v1+v2) temporary object.
1 | auto& ptr3 = &val; // Wrong! can't bind lvalue reference to rvalue |
- but we could bind const lvalue reference to rvalue
1 | const auto& ptr3 = ptr + 5; // correct! const save us a lot! |
why it works?
because you are not changing it anyway, so it is fine.
move constructor and assignment
why rvalue are key to move semantics?
- lvalue is NOT disposable, so you can copy from, but definitely cannot move from.
- rvalue is disposable, so you can move from
1 | // StringVector(StringVector&& other) noexcept; |
- rhs itself is actually lvalue, so we need to std::move
swap
The first very primitive attempt:
1 | template<typename T> |
- Bad! make three copies, sadly.
with move, second attempt with the knowledge of rvalue reference:
1 | template<typename T> |
- Cool! We use no copy with move!
Rule of Five
- If you declare one, you should declare all of them.(plus move constructor and move assignment)
Inheritance
Namespaces
1 | namespace Lecture{ |
Motivation
interface
1 | class Drink{ |
- cannot be instantiated
Abstract Classes
cannot be instantiated
has at least one pure virtual function
Non-virtual Destructors
1 | class Base{ |
Templetes vs. Derived Classes
When to use each?
static vs. dynamic polymorphism
- Templetes: at complile time generate real codes
- Derived Classes: at run time
RAII
How many code paths are there ?
Can you guarantee there is no memory leek in the code?
- For normal code paths, this code is fine.
- But if there is an exception, we will never reach the
delete e
.
How to enforce exception safety?
RAII: Resource Acquisition Is Initialization
(Scope Based Memory Management) (Constructor Acquires, Destructor Releases)(Pointer to Implementation)
What is RAII?
- all resources should be acquired in the constructor
- all resources should be released in the destructor
an example: ifstream satisfies RAII
- you should not call close
another example
fix:
besides:
Is automatic memory management a good or bad thing?
Unique Pointer
- uniquely owns its resource and deletes it when the object is destroyed.
- can’t be copied.
Shared pointer
- Resources can be stored by any number of shared_ptrs.
- Delete when none of them point to it
- Post title:CS106L笔记:OOP
- Post author:sixwalter
- Create time:2023-08-05 11:14:26
- Post link:https://coelien.github.io/2023/08/05/course-learning/CS-106L/OOP/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.