Untitled
philosophy
- use STL
- express intent (const, parallel)
- statically safe: type of the data should be known to compiler (use {} to initialize)
- compile time check > run-time
- RAII : resource acquisition is initialization
- immutable data ( const )
- encapsulate constructs, better interface
- use more than one c++ compiler
Interfaces
avoid non-const global variable
for:
- testablity
- concurrency
- optimization
avoid Singleton. Use dependency injection
Dependency injection:
- constructor
- setter
- (template param)
1 | class Client{ |
Good interfaces
- explicit
- precise\strongly typed
- low function params
could use Pimpl(pointer to implementation), if implementation is often changed. Avoid recompilation.
1 | class Widget{ |
Function
Good name
verb+object / verb or too long
constexpr
evaluated at compile time.
inline
result -> read-only, thread safe
pure function
1 | constexpr auto gcd(int a, int b){ |
use noexcept
if the function should not have exception. (if happens will terminate)
use pure function
same input -> same output
for :
- isolated test
- result cached
- reordered or executed on threads
parameter passing
I
1 | void f1(const std::string& s)// use const & |
Forward
in factory function
1 | template <typename T, typename ... T1> //variadic templates |
More explanation:
- compiler will deduce the T1 … typename by the argumentsthe create function will be deduced to:
1
create<Mytype>(myZero, 5.5, true);
1
Mytype create(int &&, double &&, bool &&){}
- T1&& will perserve the lvalue or rvalue property when forwarding
- use … before T1 to pack, after T1 to unpack
- use
std::forward
to perfect forwarding a value. (copy when lvalue, move when rvalue);
IN and OUT (modify in function)
use non-const &
1 | void addone(std::vector<int>& v){ |
OUT
use struct or tuple
use structured binding to unbind (like python).
1 | auto [iter, inserted] = mySet.insert(2011); |
Ownership semantic
- func(value): func owns a copy of value
- func(pointer* ) or func(ref& ): fumc borrows the ownership
- func(std::unique_ptr): func becomes the only owner. (moved to function)
- func(std::shared_ptr): func becomes a shared owner
the ownership should be documented => for destruction
the std::move of unique_ptr is for moving ownership
1 | { |
Return Value
- Return a T* to indicate a position ( a tree node ), a linked list node …
- chain operation, like << >> , return a T&, don’t return a reference to local
- don’t return T&& and std::move(local).
Lambda
use lambda to be more expressive
1 | std::sort(myStrVec.begin(),myStrVec.end(), |
Capturing
- capture by reference => used locally
- don’t capture by reference => nonlocally( returned, stored on the heap, passed to another thread)
correct version of thread lambda1
2
3
4std::string("C++ 11");
std::thread thr([&str] {std::cout<< str << '\n';})
thr.join(); //wait the thread to be joined
Use default argument over overloading. (DRY)
1 | void print(const string& s, format f = {}); |
Class
Rules
- orginize related data into structures.(Point, Info, …)
- struct: default public; class: default private;
- use class => logical relationship, and invariant between members.
- checked the invariant in constructor.
- differentiate the public and private methods. Use public methods as interface. Minimize the exposure.
- Only if a member function need access to private section of a class, make it a member.
- Write helper function in the same namespace with the class.
1
2
3
4
5namespace Chrono{
class Date{ ... };
bool operator == (Date, Date);
Date nextDate(Date);
}
Constructor, assignments and destructors
Big Six:
- X()
- X(const X&): copy constructor
- operator = (const X&):copy assignment
- X(X&&): move constructor
- operator = (X&&): move assignment
- ~X()
- use default operation if you can.
- TLDR: define all big six or all delete them. Reasons:
- when you define destructor,copy cons, conpy assign the compiler will not generate move cons and move assign. and fall back to a copy assign.
- when you define a mov cons or mov assign, compiler will delete the copy cons and copy assign.
- Be carefully with shadow copy.
Constructor
- a constructor should generate a fully initialized object. (don’t use init(), use delegate constrution)
- use member initializers. => lower code repetition.
All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.