C++_basics - RicoJia/notes GitHub Wiki
-
sizeof: returns the number of bytes,
-
works with array, but not ptr.
int arr[]={1,2,3,4,5}; int* arr_ptr = arr; cout<<sizeof(arr)<<endl; //works cout<<sizeof(arr_ptr)<<endl; //doesn't work
-
works with array, but not ptr.
-
=
:doesn't have a defined order of evaluation!individual_camera_feeders_[params->rtsp_url_] = std::make_unique<IndividualCameraFeeder>(std::move(params), bag_mode_); //params may be moved before params -> rtsp_url_!
-
I made an "enum" for C++17, built on top of range-based loop
for (auto i : iterable){} // Is equivalent to for (It it = iterable.begin(); it != iterable.end(); it++){ auto i = * it; }
- So we wrap this iterable, with an iterator that shows the index
// requires T to have default ctor // SFINAE with typename #include <tuple> template <typename T, typename TIter = decltype(std::begin(std::declval<T>())), typename = decltype(std::end(std::declval<T>())) > constexpr auto enumerate(T&& iterable){ struct iterator{ size_t i; TIter iter; bool operator != (const iterator& other){return other.iter != iter; } void operator ++(){ ++i; ++iter; } auto operator *(){ return std::tie(i, *iter); } }; struct iterable_wrapper{ T iterable; auto begin(){return iterator{0, iterable.begin()}; } auto end(){return iterator{0, iterable.end()}; } }; return iterable_wrapper{std::forward<T>(iterable)}; } //usage: for (const auto&[index, item]: enumerate(vec)){ ... }
- So we wrap this iterable, with an iterator that shows the index
-
subscription operator []
- if we use [],we assume the object has a valid name, and a valid memory address, and that means we are working with lvalues
- So for lvalues, returning a reference allows you to: 1. modify the value 2. more efficient
class Array{
...
int& operator [] (unsigned int i){return arr[i];}
}
- assert
#include <cassert> assert(0);
- Uses
- replace typedef
typedef std::unique_ptr<int> p; using p = std::unique_ptr<int>;
-
[REVIEW] Template Alias: short hand for a templated struct or class
template <typename T> struct MyList{ std::list<T> list_; }; //It's just a std::list with a templated param. //Use: template <typename T> using MyList = std::list<T>; MyList<int> ls;
- replace typedef
========================================================================
========================================================================
- Basics
- Structure
ostream -> ofstream -> fstream (so fstream can access both) istream -> ifstream
- Note: so if you have #include <fstream>, you can get both ifstream and ofstream.
- You can only write to any output stream, and read from any input stream.
- cout
- is thread-safe.
- is a stack (LIFO).
- General Cautions
- Do NOT copy streams, because a stream might contain bits that have & haven't been read, copying a stream doesn't make much sense. The copy constructor of stream has been disabled.
- automatically destructs when goes out of scope (exception safe)
//write to file ofstream write.open("test3.txt) write << "Scores " << 85 << " " << 73 << " " << 99 << endl; write.close();
-
std::setw
: set output stream width#include <iostream> #include <iomanip> std::cout <<"setw(6): [" << std::setw(6) << 42 << "]\n" // see setw(6): [ 42]
-
std::setfill
, sets the default char to something#include <iostream> #include <iomanip> int main() { std::cout << "default fill: [" << std::setw(10) << 42 << "]\n" // see default fill: [ 42] << "setfill('*'): [" << std::setfill('*') // setfill('*'): [********42] << std::setw(10) << 42 << "]\n"; }
- print stuff in hex using
std::hex
std::cout << "The number 42 in octal: " << std::oct << 42 << '\n' << "The number 42 in decimal: " << std::dec << 42 << '\n' << "The number 42 in hex: " << std::hex << 42 << '\n';
-
Basic Example
#include <iostream> #include <fstream> #include <string> using std::cout; using std::endl; std::ifstream read("hehe.txt"); std::string line; if (read.is_open()){ while(getline(read, line)){ cout<<line<<endl; } read.close(); }
-
read chars into vector
-
istream_iterator
is an input iterator, its++
uses operator>>
- it has an end of stream iterator, which is constructed out of nothing.
#include <iostream> #include <iterator> #include <fstream> #include <vector> std::ifstream is("numbers.txt"); std::istream_iterator<double> start(is), end; std::vector<double> numbers(start, end);
-
fopen(file_name, "wb")
will erase the old content, then appending new content onto it.
cout<<uint8_t will print a char!
-
std::ifstream
is also not copyable but movable
========================================================================
========================================================================
-
Basics
- enum color {black, white} is callled "unscoped enum (C++98)"
- scoped enum's Default type is int, unscoped enum doesn't have a default underlying type. But you can specify them for both cases.
-
Unscoped Enum vs scoped enum [REVIEW]
-
Caution: unscoped enum leaks names, while scoped enum doesn't pollute namespace
enum Color{red, black}; auto red = false; //red has been defined in enum, you can't use it Color c = red; //this is legal, but will polute namespace enum class Color_2{white}; auto white = false; //this doesn't leak names. Color C = white; //Not good!! Color C = Color::white; //this is findSmaller auto C = Color::white; //also fine
-
Caution: unscoped enum may convert to double implicitly, while scoped enum doesn't have such a problem
- So you must use static_cast to explicitly convert it.
enum Color{ black, red }; Color c = red; if (red < 14.5); //THIS IS LEGAL, so it's bad Color_2 d = Color_2::red; if (d < 14.5); // illegal if (static_cast<double> d < 14.5)
- So you must use static_cast to explicitly convert it.
-
Caution: unscoped enum requires extra work to be forward-declared, scoped enum doesn't
- unscoped enum doesn't have a default underlying type, for scoped type, the default is always int.
- So compiler needs to determine unscoped enum's size
- Usually compiler will find smallest type such as char, but it might trade size for time.
- So if you want to forward declare unscoped enum, you must specify size
- You can also specify size on scoped enum for forward declaration
enum Color{ red = 0 black = 0xFFFFFFFF }; //This may happen enum class Color_2 {red, black}; // In another file enum Color: std::uint32_t; //this is valid enum Color_2; // This is valid enum Color_2: std::uint32_t; //this is also valid
-
Advantage of unscoped enum: design pattern for looking up a field in tuple, using its implicitly conversion [REVIEW]
using UserInfo = std::tuple<std::string, std::string, std::size_t>; enum Fields{uiname, uiEmail, uiReputation}; //store the field number 1,2,3 as uiname ... UserInfo info; auto val = std::get<1>(info); // I can't remember what field 1 was ... auto val = std::get<uiname>(info);
- Using scoped enum will be much more verbose:
enum class Fields{uiname, uiEmail, uiReputation}; //store the field number 1,2,3 as uiname ... auto val = std::get<static_cast<std::size_t>(Fields::uiname)>(info);
- Using scoped enum will be much more verbose:
-
frequently-changed enum can have a reference
enum Foo{ Field_One = 1; Field_Two = 2; __First__ = Field_One; // in the future you may have a different field }; vec.at(__First__) is a good practice
-
enum class is strongly typed. So even though the underlying type is
unsigned int
, it still cannot be converted to unsigned int directly. So use unscoped enum if you wishenum class Foo : char{ STUFF } char i = Foo::STUFF; //wrong. use static_cast
- unscoped enum
enum Foo{ STUFF; }; int i = STUFF; //fine int i = Foo::STUFF; // fine
- unscoped enum
-
========================================================================
========================================================================
#include <iostream>
#include <iomanip>
#include <ctime>
int main()
{
std::time_t t = std::time(nullptr);
std::tm tm = *std::localtime(&t);
std::cout << "ru_RU: " << std::put_time(&tm, "%c %Z") << '\n';
}
- POD (plain old data, i.e, no ctor or dtors)
========================================================================
========================================================================
- order of inclusion in hpp: if
A.hpp
should includeB.hpp
but doesn't, then this inc.cpp
may "cover" this problem, which is not good:#include "B.hpp" #include "A.hpp"
========================================================================
========================================================================
- Passing function as argument and having default argument:
func( double another_func = func2)
- should only be defined once in class declaration or definition, but not at the same time!!
-
一个完整的function call 会callee give the function control -> cpu find the instruction addresses, puts them on stack -> execute function instructions -> return the values to predesignated memory-> return control to the callee function.
-
当你return control to the callee function 相对耗时很长时(你实际function很短),你可以减少这个control 所谓switching time (overhead),因为这些短的程序可能会被经常调用。所以你可以搞inline func(){}... 这个inline是一个request(不是一个command) 给compiler,让compiler把整个程序copy到callee里边。
-
Inline function - 【one definition rule】所以你要在h file 中把整个的declaration 都要写出来,不然有可能会有overloading【???】
-
Header File: 每个std中的类型都有一个header file。如std::string,所以你要 #include
-
In a class,
- functions defined inside the class is implicitly inline. So this is bad:
class Foo{ public: inline int lol(){return 1; } //inline is redundant here }
This is good:
class Foo{ public: int lol(); } inline int Foo::lol(){return 1; } //this is good.
- Virtual functions cannnot be inline, since they're resolved during run time.
- override is an identifier that enforces cpp to check for a virtual base class. If there's not one, there will be an error.
- switch case? [REVIEW]
- break is optional. If omitted, control will go on until reaching the next break.
- default is optional too.
switch(var) case 1: break; default:
- try-catch
- you can have multiple catch statements:
try { // code here } catch (int param) { cout << "int exception"; } catch (char param) { cout << "char exception"; }
- you can have multiple catch statements:
========================================================================
========================================================================
-
C keywords
- static
- auto (automatic, in functions, etc.)
- register
-
thread_local
-
global/static variable but actually has a copy in each thread
-
example
thread_local int i; //global thread_local class Foo{ static thread_local int j; //thread_local class members }; static thread_local int Foo::j; void foo(){ thread_local int k; //thread_local class variables }
- k is automatically a "static" variable during the life of a thread.
- k will be initialized the first time
foo()
is called.
- k will be initialized the first time
-
i
andj
will be initialized before they're first used, but we don't know when exactly - if we don't use them, they may not be constructed at all (compiler dependent). -
i, j, k
are zero initialized, like static variables. - if
i,j,k
throws errors during construction,std:;terminate
will be thrown.
- k is automatically a "static" variable during the life of a thread.
-
Useful in random number generator.
- you need a seed for each thread. It can appear static/global, but actually each thread has a copy
- If you use a true global variable, other threads can interfere.
- local variable will get initialized everytime, so you get the same number.
- Only variable name matters
-
When you pass in a pointer, it's just a normal pointer
thread_local int i=0; void thread_func(int*p){ *p=42; } int main(){ i=9; std::thread t(thread_func,&i); t.join(); std::cout<<i<<std::endl; // now you get 42 }
-
-
Static Global Variable
- static global variable in cpp file, has scope limited to the current cpp file. So header including extern accidentally will not complain about multiple-definitions.
// header.hpp" extern int i; // src1.cpp #include "header.hpp" static int i = 3; // no multiple definition //src2.cpp #include "header.hpp" int i = 4;
- Golden Rule: static keyword is only used with the declation, never in the definition.
class Foo{ static void bar(); }; static void Foo::bar(){} //error: cannot allow to have static linkage, remove static
- static global variable in cpp file, has scope limited to the current cpp file. So header including extern accidentally will not complain about multiple-definitions.
- 但是你要有explicit,即explicit foo(int n,int m=20):num(n),num2(m){},那么这种type conversion 就被禁止了,你只能有foo bar(3) 而不是foo bar = 3;
- When a ctor has only one argument, then it becomes a conversion ctor
- So this ctor can either implicitly or explicitly cast the single argument to an object
-
Explicit will ban (and only) ban that implict conversion
class Foo{ explicit Foo(double r = 0.0, double i = 1.0); bool operator== (Foo rhs); } int main(){ Foo f1(3.0); if (com1 == 3.0) //Without explicit, this is legal: 3.0 will be IMPLICITLY converted to a temp Foo object. // Now with explicit, this is banned. ... if (com1 == (Foo)3.0) // This is still legal, cuz explicit will ONLY ban implicit conversion. }
- explicit 只能用在class definition 里面。
- explicit: 只有constructor, conversion operators need this
- When a ctor has only one argument, then it becomes a conversion ctor
-
Motivation (C++11)
- We want to prohibit using some functions.
-
Basics
- delete works on ANY FUNCTION
- in C++98, there's an equivalent - declaring a class and delibrately having it undefined
- but that doensn't work outside classes, and not fully inside class.
-
Uses & Comparison with C++98 equivalent
- Use in a class - the only functions we don't want is the automatically generated ones. Say we don't want users to use copy constructors
class Foo{ public: Foo& (const Foo&) = delete; //C++11 way, the better way private: Foo& (const Foo&); // C++98 way, so either no access to this function, or child/friend classes will get an error. }
- As a convention, delete functions in a class is always place d in public
- That's because on some compilers, they might complain about the function being private. So here you have a better error msg.
- In C++98, class is the only place that you see a declaring undeclared private function, no other places, while =delete can be used anywhere, on any function
- In C++98, if there's an error, you'll only see that during link-time.
- Non-member functions - C++98 cannot deal with this
bool isLuckyNumber(int number){...} // This is what we want bool isLuckyNumber(char number) = delete // Not what we want bool isLuckyNumber(double number) = delete // Not what we want
- Caution: float will be banned here too as a side effect, because **float will always implicitly convert to double** ```cpp float i = 3.5; isLuckyNumber(i); // Not work! ```
- Templated Functions
template <typename T> void processPointer(T* ptr){ ... } //Now we want to ban void*, char*, because 1. you can't increment void*, 2. char* is always a pointer to a c_str. template <> void processPointer(char* ptr) = delete; template <> void processPointer(void* ptr) = delete; template <> void processPointer(const char* ptr) = delete; template <> void processPointer(const void* ptr) = delete;
- In a class, you **CAN'T** do it in C++98 way, because template specialization cannot be private. ```cpp class Foo{ public: template <typename T> void processPointer(T* ptr){ ... } template <> void processPointer(const void* ptr) = delete; //C++11, Works! private: template <> void processPointer(const void* ptr); //C++98, error } ```
- NULL vs nullptr
- NULL is a just macro of integral type, with values 0 or 0L (long int). These values are also called "null pointer constants". In C, Null could also be
(void*) 0
;void castSpell(int spellID); castSpell(NULL); //this works!
- Other dangers of NULL or 0:
- other programmers don't know if this is pointer type
- Yeah NULL or 0 works with smart pointers individually, but templates can only deduce them as int type. Which causes a problem
template<typename FuncType, typename PtrType> decltype(auto) lockAndCall(FuncType func, // C++14 PtrType ptr) { return func(ptr); } void func(std::unique_ptr<int> ptr_1){} int main() { lockAndCall(func, NULL); //this causes problem return 0; }
- nullptr is
- a const expression evaluated at value 0, of type std::nullptr_t
- convertible to pointer types, but it's not an integral type.
- Always use nullptr instead of NULL!
- NULL is a just macro of integral type, with values 0 or 0L (long int). These values are also called "null pointer constants". In C, Null could also be
-
Motivation:
- a function specifier to tell the compiler: it's okay to generate some optimized code for this function.
- So stack-unwinding is ommited for functions declared
noexcept
.
-
Basics
- noexcept is just a compile time specifier, so code will be optimized.
- Or noexcept(Some_func); can be used to tell if a function has noexcept
- potentially throwing vs non-throwing code:
- potentially throwing:
- regular funcs,
- user-defined constructors
- operators such as new
- Non-throwing: if a user defined one uses a potentially throwing func, it will become potentially-throwing
- default ctor
- The big five (copy ctor, -, move ctor, =, dtor)
- potentially throwing:
- e.g
noexcept(5+3); //return true, int are non-throwing noexcept(some_struct{}) //return true, default ctor is always non-throwing void func() noexcept{} noexcept(func()); //returns true void fake_noexcept() noexcept{std::throw -1; } noexcept(fake_noexcept()) //still return true, because noexcept just checks the compile time guarantee of things. run-time doesn't matter. void no_gurantee_func(){} noexcept(no_gurantee_func()); //return false
- potentially throwing vs non-throwing code:
- If there IS one exception coming out of a function declared noexcept, then
std::terminate
is called, no stack-unwinding is called. - C++98 style:
int lol(params)throw(); int lol(params)noexcept;
-
exception safety levels:
- no fail: function never fails, such as std::move, std::swap, erase, unique_ptr operations,
- no throw: it might be able to fail, but no exception is emitted, like: destructors, memory deallcation, cleanup functions.
- strong guarantee: exception can emit, but no memory leaks, program state will remain unchaged.
-
Usage
- to permit a function to/not to propagate exeptions
void bar2 () noexcept{throw std::runtime_error("lol"); } void foo() noexcept(0==1) {throw std::runtime_error("oops"); } //this is always going to propagate, as 0==1 is false, so we don't switch on noexcept void bar() noexcept(1==1){throw std::runtime_error("oops"); } // always no exception emitted from ths function. equivalent to bar2.
- Actually, std::vector, and other STL containers, do need NOEXCEPT for move semantics; otherwise, copy is used SO DECLARE YOUR MOVE CTOR noexcept, if you're sure!!!
- std::vector, std::deque, has insert, push_back... they all make "strong exception gurantee ": it's okay to fail, but the programs state will be restored.
- With non-noexcept copy ctor, this is achievable: you copy all vector elements back to their original location.m
- But move is different: once you've moved an obj, moving the objects back to their locations might yield problems too.
-
Cautions:
- It's easy to add noexcept at the beginning, but it's hard to take it off, as it's part of the interface. your clients will rely on this.
- If you're not sure whether to include it, just don't
- run_time error
#include <exception> try { if (badThingHappened) { throw std::runtime_error("Something Bad happened here"); } } catch(std::exception const& e) { std::cout << "Exception: " << e.what() << "\n"; }
- ' (C++14)
int i = 100'000;
- automatic variable: allocates and deallocates automatically when flows in/out of a scope.
- other types include static and dynamic
- with optimization, "stack variables" may not live on the stack. Instead they're in registers. ========================================================================
========================================================================
- static_cast: your compilation will fail, if bool (has to be available during compile time) is false (c++ 11)
static_assert(1>2, "hehe"); //error: static assertion failed: hehe
- Pros: better than
#error
, becausestatic_assert
happens after pre-processing - Cons: requires constexpr. To enforce that, need to be in a template function.
template<typename T> void callback_check(const ConnectorClientCallbacks& cbs){ static_assert(cbs.fill_discovery_msg != nullptr, "Discovery Client: fill_discovery_msg is required from both sub and pub"); }
- Pros: better than
- Can executables compiled in c++11 use a library compiled with c++20? compile using C++20 the whole thing
-
-O3
is an optimization
-
-
What is a macro: it looks a like a function, but processed during compile time (scripted derivative). Why does it exist: because this is from C.
#define square(x) x*x
- or if you need to have more lines
#define cube(x) x * \ x * x
- or if you need to have more lines
-
It's dangerous, because it is a simple substitution. example:
int a = 3; square(a+2); // you think it will be (3+2) * (3+2), but actually it will output 3 + 2 * 3 + 2, because it's simple substitution. one fix is #define square(x) (x)*(x)
- The first step is a simple string substitution, no semantic testing
-
Macro invokation do not need ; at the very end. So you want to use that as a distinction from regular functions. Also, name is mSOMETHING(). Otherwise, you might get funny compilation msgs.
-
What do you do instead? use inline functions.
- Inline func is also expanded at the time you invoke it. But Inline is processed by compiler (compilation), while macro is expanded by pre-processor.
- the function is copied and expanded to the function call.
- Inline can access all members in a class, macros do not.
- Short class member functions will automatically go onto inline.
- Inline func is also expanded at the time you invoke it. But Inline is processed by compiler (compilation), while macro is expanded by pre-processor.
- basic
#include<bits/stdc++.h>
std::bitset<16>bin_num(integer);
bin_num.count(); //# of 1s
- i = (i<<2); Don't forget that i<<2 cannot modify i!!
- Bit field: Can effectively reduce the size of a struct. coming from C
// C
struct {
unsigned int age : 3; //only 3 bits long
} Age;
int main(){
Age.age = 4; // will get 4
Age.age = 8; // will get 0
}
//C++
Age A;
A.age ...
-
memset
#include <cstring> memset(void *str, int ch, size_t n); Copies n char to str.
- undefined behaviours:
- if n is greater than what str can hold
- ch cannot be copiable (like not a c struct, array, scalar)
- DO NOT USE THIS IN INT ARRAYS!!! it works by writing byte by byte, (int has 4 bytes, so you only want to write once!!)
-
memcpy (void* destination, void* src, size_t num); //copies value directly.
-
cmath functions:
std::pow, std::log2
#include<climits> INT_MIN; #include<cmath> std::pow(2, n); //2^n log2(bin_num) //the most significan bit. #include<cstring> std::memset; printf("%u, %lu, %c", unsigned, long_unsigned, char);
-
Logging:
- FILE, FUNCTION are useful for generating logs
- they're macros
#include<iostream> using namespace std; int errorLog (const char* file, const std::string& msg){ cerr << "[" << file << "] " << msg << endl; } #define LOG( msg ) errorLog( __FILE__, msg ) main() { LOG("This is a dummy error"); }
-
Sleep
#include <unistd.h> sleep(10); //sleep for 10 seconds
========================================================================
========================================================================
- exit
- exit() exits the whole program, where return just returns to the caller stack. exit(0) means successful, exit(1) means unsuccessful.
- X86 uses little endian, some ARM uses big Endian
========================================================================
========================================================================
-
segfault
- might caused by infinite loop (stack overflow). weird thing is step will return segfault even if an infinite loop is not here but behind it
- You may not have a constructor for your input.
- pointer might be null. So a good habbit is to always do a pointer check
-
expected initializer before - You might have forgotten a ";"
-
undefined reference to some function:
- 有可能是你namespace 没搞对
- compiler 不兼容
- 还有就是input type 不对,比方说geometry_msgs::TransformStamped 放到geometry_msgs::Transform 里边。
-
xx does not name a type
- check if CMakeList.txt has properly imported it
- check if your header has included it (your header file may be wrong!!)
- check if you already have a namespace.
-
colon : is used when you have
- a constructor, you need to initialize it
- inheritance
-
vector v(3);, not v{3}! that's initializer.
-
SUPER TRICKY: do not do (unsigned int i = 2; i >= 0 --i). because once i is below 0, it will actually be 4294......! use int instead for all i.
.class{}**;**
- I need to specifically do: mtx_ptr_(std:: make_uniquestd::mutex) to allocate resources in constructor?
-
constness
- **always have const member functions, so you can have const objects. Inside those const functions, make sure you use const member functions. **
- Else you may see an error like this
- you don't need all members variables to be const if you wanna have const object.
-
"Implicit declaration of function": you're missing a function declaration in .h file
-
Do not have the same variable and function name!! Else they'll be mixed
-
When you see
_Allocator...
, you should know this is vector resizing. -
Invalid use of incomplete type
- you must include <.h> file, otherwise you see the error- Incomplete type: type whose size cannot be determined.
- to create an incomplete type:
struct student*ps;
- Declare the same type later.
-
multiple definition: (see c++ compilation)
-
when you do
cout<<some_unsigned_char
, it may not be displayed! Need to convert it to int -
error: invalid use of destructor '~util::Camera_Streamer' as a type
- You might have
~Camera_Streamer::Camera_Streamer() {}
- Correction will be
Camera_Streamer::~Camera_Streamer() {}
- You might have
-
invalid declarator before '&' token
- maybe you forgot namespace innamespace::func()
-
-
double best_possible_d = 0;
: must have initialization, else pod won't initialize!
-
========================================================================
========================================================================
- reminder: pass in
this->member
in to a function object, likeauto future = std::async(&File_Writer::async_file_write, this, std::reference_wrapper<Buf_Ptr>(this->buffer_), true);
-
std::async(func)
needs func to be copyable - therefore if you have a moveable-only object, store it as reference
-
-
std::accumulate(beg, end, init)
, defined in<numeric>
-
remove(file)
in<cstdio>
- if
std::istream
is not closed, next timeopen()
will not succeed!!
========================================================================
========================================================================
- check type if you don't get what you want:
bool a = 10; //ofc you don't get the right thing
- Do not use memset.
- str.substr is powerful.
- heap overflow means you're trying to access a non-existent arr element.