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::hexstd::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_iteratoris 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::ifstreamis 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.hppshould includeB.hppbut doesn't, then this inc.cppmay "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
-
iandjwill 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, kare zero initialized, like static variables. - if
i,j,kthrows errors during construction,std:;terminatewill 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::terminateis 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_asserthappens 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
-
-O3is 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->memberin 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::istreamis 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.