cpp温故知新(c++20)
gerneral all
storage duration
automatic storage duration The storage for the object is allocated at the beginning of the enclosing code block and deallocated at the end. All local objects have this storage duration, except those declared static, extern or thread_local.
static storage duration The storage for the object is allocated when the program begins and deallocated when the program ends. Only one instance of the object exists. All objects declared at namespace scope (including global namespace) have this storage duration, plus those declared with static or extern.
thread_local storage duration The storage for the object is allocated when the thread begins and deallocated when the thread ends. Each thread has its own instance of the object. Only objects declared thread_local have this storage duration. thread_local can appear together with static or extern to adjust linkage.
dynamic storage duration The storage for the object is allocated and deallocated per request by using dynamic memory allocation functions.
linkage
no linkage The name can be referred to only from the scope it is in. variables in block scope(except extern)
static linkage The name can be referred to from all scopes in the current translation unit.
variable in namespace scope 1. variabales in namespace scope declared static 2. const-qulified(or constexpr) variable not declared extern 2. data member in anonymous unions 3. variables in anonymous namespace
- external linkage other variable in namespace scope
perfect Forwarding
it used to reserve the r or l value property of a parameter in generic context.
emplace_back vs push_back
- emplace_back cannot use to initilize aggregated things.(up to c++17, it is specialized to none list in-place construction)
container
- vector vs deque for small size use vector, otherwise use deque instead.
new features
structured binding auto [res1, res2] = ...
init statement of if or switch
constexpr if
- c++11 unknow before
- function reload
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26class Test {
private:
std::string name{"wens"};
public:
std::string getname() && { printf("in && function\n"); return name;}
const std::string& getname() const& { printf("in const& function\n"); return name;}
};
int main() {
Test t;
auto v = t.getname();
printf("%s\n", typeid(v).name());
auto a = Test{}.getname();
printf("%s\n", typeid(a).name());
getchar();
return 0;
}
constexpr
may be available at compile-time but not guarantee
consteval
must evaluate at commpile time
1
2
3if consteval {
}you cannot evaluate runtime functions at compile-time
value categories
expression -> glvalue(generalized lvalue) and rvalue glvalue -> lvalue and xvalue(expiring value) rvalue -> xvalue and prvalue(pure rvalue)
simple: lvalue: everything has name and string literal (identiy of an object) prvalue: temporaries and other literal (perform initialization/compute an object) xvalue: value from std::move() (denote an object whose resource can be reused) glvalue produce locations, prvalue preform initialize
c++17: prvalue perform initialization -> no temporary object yet glvalue produce locations materialization to get temporary object -> prvalue to xvalue conversion
lambda funcion vs std::function
- std::function cannot inline by compiler
- when lambda with capture assigned to std::function, std::function will do heap allocation(some implementation may not heap-alloacte when less than specific threshhold)
sizeof & alignof
- alignof(T) = max(alignof(member1), alignof(member2)...)
- sizeof(T) % alignof(T) == 0
determine compile type
_MSC_VER
__clang__
__GNUC__
copy ellison for initialization from temporaries(prvalues) is required in c++17
callable copy/move constructor no longer required
1 | class Test { |
auto decays
- raw arrays convert to pointers
- functions convert to function pointers
- top-level references are removed
- top-level const/volatile are removed
initialization
to pass mutiple parameters to ordinary constructors different types ok
as an std::initializer_list<> must be homogeneous(same types or can conversion to the same type) only for {}
- () initialization for ordinary constructor only {} initialization for all constructors
- std::initialization_list<> constructors has higher priority
- but the default constructor has the highest priority
to aggregate object wiht initializer list{}, member is value initiaized without any initializer list and no default member initialization, then uninitialized
enum initializaiont with integer
- only with direct list initialization
- if the enum has fixed underlying type
- since c++17
1 | enum class Salution{mr, ms}; |
uniform initialization is not quite work with atomic and assert(maybe fixed int c++20)
- atomic
1 | //with P0883 maybe fixed in c++20 |
- assert
1
2
3assert(c == std::complex(0, 0)); //ok
assert(c == std::complex{0, 0}); //does not compile
assert((c == std::complex{0, 0})); //ok
compiler related
- registers rax(64) -> eax(32) -> ax(16) -> ah(8) and al(8) (left bits zeroed)
rax
: return value rdi
: 1st param rsi
: 2nd param
- Underscore
(_)
is special, as C++ reserves the following to be used by the compiler and the standard library
- any identifier that starts with an
_
followed by an upper-case letter, and - any identifier that contains two consecutive underscores
(i.e. __)
anywhere in its name
- platform judge
1
2
3
4
5
6
7
8
9
/* we're on windows */
/* we're on gcc ! */
/* we're on clang */
/* ¯\_(ツ)_/¯ */
practical performance practices
- const & initialize
- always const
- always initialize
- use IIFE can help you initialize
- don't recalculate values that can be calculated once
- list vs vector vs array vs containers
- always prefer std::array
- then std::vector
- others
- avoid object copying
- avoid automatic conversion: using conversion explicit
- don't pass smart pointers
- avoid std::endl
- prefer return std::unique_ptr
to std::shared_ptr
shared_ptr thread safe
spinlock
- in user mode or general application mode, you should avoid spinlock.
- in kernel mode(low-level stuff), you only hold the lock about several instructions and latency is important, a spinlock maybe better solution than a lock.
c++17&c++20 new features
c++17 fold expressions, typename in template parameters, auto for non-type template parameter, class template argument deduction inline variable(constexpr imply inline now), nested namespace
c++20 template lambdas, string literals as non-type template parameters, constrains(template requires), concepts
GotW by herb
overload、override& hide
static type and dynamic type
virtual destructor
- overload resolution & default parameter are on static type.
- override is on dynamic type.
- virtual dtor > [In delete b], if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.
1
2
3Base *b = new Derived();
// use b
delete b; // Here's the problem!
If you want to prevent the deletion of an instance through a base class pointer, you can make the base class destructor protected and nonvirtual; by doing so, the compiler won't let you call delete on a base class pointer.
Because once execution reaches the body of a base class destructor, any derived object parts have already been destroyed and no longer exist. If the Base destructor body were to call a virtual function, the virtual dispatch would reach no further down the inheritance hierarchy than Base itself. In a destructor (or constructor) body, further-derived classes just don't exist any more (or yet).
inline function
the declaration and definition are the same. > so the member function which definition with declaration maybe treated as inline by compiler
function modifiers
- const
1
2
3
4
5
6
7
8// value parameter: top-level const is not part of function signature
int f( int );
int f( const int ); // redeclares f(int): this is the same function
// here you can declare f(int), then when define it, using f(const int) to refer const of the parameter
// non-value parameter: top-level const is part of function signature
int g( int& );
int g( const int& ); // overloads g(int&): these are two functions
header include
- parameter and return types only need to be forward-declared
dynamic_cast
used to downside_cast(from base to derived), or sidecast
- dynamic_cast<void*> cast to most-derived type
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
struct base1 {
base1() {printf("in base1 constructor\n");}
virtual ~base1() {}
};
struct base2 {
base2() {printf("in base2 constructor\n");}
virtual ~base2(){}
};
struct derive: base1, base2 {
derive() {printf("in derive constructor\n");}
};
derive *ptr = new derive();
base1 *base1_ptr = ptr;
base2 *base2_ptr = ptr;
auto address_of_derived = dynamic_cast<void*>(base2_ptr);
printf("base1_ptr: %x\n", base1_ptr);
printf("base2_ptr: %x\n", base2_ptr);
printf("ptr: %x\n", ptr);
printf("address_of_derived: %x\n", address_of_derived);
[base1] [base2] [derive]
| |
dynamic_cast<void*> base2_ptr
derive_ptr
base1_ptr