├── .gitignore ├── examples ├── basics │ ├── basecode.cpp │ └── type_inference.cpp ├── function_templates │ └── basecode.cpp ├── casting │ └── casting.cpp ├── decltype │ └── basecode.cpp ├── function_pointers │ ├── overloaded.cpp │ └── basic.cpp ├── regexes │ └── example.cpp ├── pointers │ └── basecode.cpp ├── default_args │ └── basecode.cpp ├── async │ ├── basecode.cpp │ ├── locking_rewrite.cpp │ └── locking.cpp ├── structs │ ├── structs.cpp │ └── returning.cpp ├── smart_ptr │ ├── shared_ptr.cpp │ └── unique_ptr.cpp ├── inheritance │ └── basecode.cpp ├── lambdas │ └── lambdas.cpp └── rvalue_refs │ └── basecode.cpp ├── README.org └── learning_cpp.org /.gitignore: -------------------------------------------------------------------------------- 1 | # emacs 2 | *~ 3 | *# 4 | .#* 5 | a.out -------------------------------------------------------------------------------- /examples/basics/basecode.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | std::cout << "This is a test of your local emergency broadcasting system.\n"; 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /examples/function_templates/basecode.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template void foo(T param) { 4 | std::cout << param << "\n"; 5 | } 6 | 7 | int main() { 8 | 9 | foo(20); 10 | foo(20.3); 11 | foo("hello world!"); 12 | 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /examples/casting/casting.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | double some_num = 1.4; 5 | int a = (int) some_num; 6 | int b = static_cast (some_num); 7 | std::cout << "c-style cast: " << a << "\n"; 8 | std::cout << " static cast: " << b << "\n"; 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /examples/decltype/basecode.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* 5 | * To build: 6 | * 7 | * > g++ -std=c++11 basecode.cpp 8 | * 9 | */ 10 | 11 | int main() { 12 | auto floob = "This is a test!"; 13 | decltype(floob) thingy = "hello world"; 14 | std::cout << typeid(thingy).name() << " : \"" << thingy << "\"\n"; 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /examples/function_pointers/overloaded.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | typedef void (*type_a)(int); 5 | typedef void (*type_b)(); 6 | 7 | 8 | void print (int a) { 9 | std::cout << a << "\n"; 10 | } 11 | 12 | 13 | void print () { 14 | std::cout << "flaming garbage\n"; 15 | } 16 | 17 | 18 | int main() { 19 | type_a foo = print; 20 | type_b bar = print; 21 | 22 | foo(10); 23 | bar(); 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /examples/basics/type_inference.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | * Note that you need C++11 to use type inference. To compile againts 5 | * C++11, use the following command: 6 | * 7 | * > g++ -std=c++11 type_inference.cpp 8 | * 9 | */ 10 | 11 | int main() { 12 | auto some_int = 10; 13 | for (int i=0; i 2 | #include 3 | #include 4 | 5 | /* 6 | * To build: 7 | * 8 | * > g++ -std=c++11 example.cpp 9 | * 10 | */ 11 | 12 | int main () { 13 | auto raw = std::string("vertex -1.624555 -4.999952 -8.506543"); 14 | std::smatch match_itr; 15 | std::regex pattern("[0-9-.]+"); 16 | while(std::regex_search(raw, match_itr, pattern)) { 17 | std::cout << match_itr[0] << "\n"; 18 | raw = match_itr.suffix().str(); 19 | } 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /examples/pointers/basecode.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | int a = 10; 5 | int& b = a; 6 | b += 10; 7 | std::cout << " 'a' is " << a << "\n"; 8 | std::cout << " 'b' is " << b << "\n"; 9 | std::cout << "'&a' is " << &a << "\n"; 10 | std::cout << "'&b' is " << &b << "\n"; 11 | 12 | int c = 10; 13 | int*d = &c; 14 | std::cout << " 'c' is " << c << "\n"; 15 | std::cout << "'&c' is " << &c << "\n"; 16 | std::cout << " 'd' is " << d << "\n"; 17 | std::cout << "'*d' is " << *d << "\n"; 18 | std::cout << "'&d' is " << &d << "\n"; 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /examples/default_args/basecode.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | /* 5 | * To build: 6 | * 7 | * > g++ -std=c++11 casting.cpp 8 | * 9 | */ 10 | 11 | 12 | int print_calls = 0; 13 | template void print (T value, const char* label = nullptr) { 14 | print_calls += 1; 15 | if (label == nullptr) { 16 | std::cout << "call " << print_calls; 17 | } 18 | else { 19 | std::cout << label; 20 | } 21 | std::cout << ": " << value << "\n"; 22 | } 23 | 24 | 25 | int main() { 26 | print(10); 27 | print(20.3); 28 | print("foo"); 29 | print(2, "1+1"); 30 | print("hello world", "some str"); 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /examples/async/basecode.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /* 8 | * To build: 9 | * 10 | * > g++ -std=c++11 -pthread basecode.cpp 11 | * 12 | */ 13 | 14 | 15 | std::mutex mtx; 16 | 17 | 18 | void worker (const char* statement) { 19 | for (int i=0; i<100; i+=1) { 20 | mtx.lock(); 21 | std::cout << statement << "\n"; 22 | mtx.unlock(); 23 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 24 | } 25 | } 26 | 27 | 28 | 29 | int main() { 30 | std::future future_a = 31 | std::async(std::launch::async, std::bind(worker, "meep")); 32 | 33 | std::future future_b = 34 | std::async(std::launch::async, std::bind(worker, "raar")); 35 | 36 | future_a.wait(); 37 | future_b.wait(); 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /examples/structs/structs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | * You can initialize a struct with the {"this", "that"} syntax, or 5 | * with a constructor, but you cannot do both in the same program. In 6 | * what follows, you can comment out the constructor function and 7 | * toggle the comments in the 'main' function, and it should still 8 | * work. 9 | */ 10 | 11 | 12 | struct WonderStruct { 13 | char *name; 14 | char *value; 15 | 16 | WonderStruct () { 17 | name = "Saint Norbert, the Hungry"; 18 | value = "dubious at best"; 19 | } 20 | }; 21 | 22 | 23 | void print(WonderStruct *chump) { 24 | std::cout << "Today we review " << chump->name << ".\n" 25 | << "It has been said that " << chump->name 26 | << "'s value as a human being is\n" << chump->value << ".\n"; 27 | } 28 | 29 | 30 | int main() { 31 | WonderStruct chump_a = WonderStruct(); 32 | //WonderStruct chump_b = {"Aeva", "something she judges too harshly"}; 33 | print(& chump_a); 34 | //print(& chump_b); 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /examples/function_pointers/basic.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | int add (int lhs, int rhs) { 6 | return lhs + rhs; 7 | } 8 | 9 | int sub (int lhs, int rhs) { 10 | return lhs - rhs; 11 | } 12 | 13 | int mul (int lhs, int rhs) { 14 | return lhs * rhs; 15 | } 16 | 17 | 18 | typedef int (*mathop)(int, int); 19 | 20 | 21 | mathop random_operation () { 22 | mathop ret; 23 | switch (rand() % 3) { 24 | case 0: 25 | ret = add; 26 | break; 27 | case 1: 28 | ret = sub; 29 | break; 30 | case 2: 31 | ret = mul; 32 | break; 33 | } 34 | return ret; 35 | } 36 | 37 | 38 | int main() { 39 | int op_count = 5; 40 | mathop script[op_count]; 41 | for (int i=0; i 2 | #include 3 | 4 | 5 | /* 6 | * To build: 7 | * 8 | * > g++ -std=c++11 shared_ptr.cpp 9 | * 10 | */ 11 | 12 | 13 | using std::shared_ptr; 14 | using std::unique_ptr; 15 | 16 | 17 | class Resource { 18 | public: 19 | Resource() { 20 | std::cout << "misc resource initialized\n"; 21 | } 22 | ~Resource() { 23 | std::cout << "misc resource deleted\n"; 24 | } 25 | }; 26 | 27 | 28 | class Thingy { 29 | public: 30 | Thingy() : res(new Resource) { 31 | std::cout << "thingy initialized\n"; 32 | } 33 | ~Thingy() { 34 | std::cout << "thingy deleted\n"; 35 | } 36 | private: 37 | unique_ptr res; 38 | }; 39 | 40 | 41 | int main() { 42 | shared_ptr foo(new Thingy); 43 | auto bar = foo; 44 | auto baz = foo; 45 | 46 | std::cout << "clearing foo...\n"; 47 | foo.reset(); 48 | std::cout << "clearing bar...\n"; 49 | bar.reset(); 50 | std::cout << "clearing baz...\n"; 51 | baz.reset(); 52 | std::cout << "exiting program...\n"; 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /examples/inheritance/basecode.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | /* 6 | * To build: 7 | * 8 | * > g++ -std=c++11 basecode.cpp 9 | * 10 | */ 11 | 12 | 13 | using std::cout; 14 | using std::shared_ptr; 15 | 16 | 17 | class BaseClass { 18 | public: 19 | BaseClass() { 20 | cout << "base class initializer called\n"; 21 | }; 22 | BaseClass(int foo) { 23 | cout << "special base class initializer called\n"; 24 | }; 25 | ~BaseClass() { 26 | cout << "base class destructor called\n"; 27 | }; 28 | virtual void splorf () { 29 | cout << "base class splorfed\n"; 30 | }; 31 | }; 32 | 33 | 34 | class ChildClass : public BaseClass { 35 | public: 36 | ChildClass() : BaseClass(10) { 37 | cout << "derived class initializer called\n"; 38 | }; 39 | ~ChildClass() { 40 | cout << "derived class destructor called\n"; 41 | }; 42 | void splorf () override { 43 | cout << "child class splorfed\n"; 44 | }; 45 | }; 46 | 47 | 48 | 49 | int main() { 50 | auto foo = shared_ptr(new ChildClass); 51 | foo->splorf(); 52 | foo.reset(); 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /examples/structs/returning.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | struct WonderStruct { 5 | char *name; 6 | char *value; 7 | }; 8 | 9 | 10 | WonderStruct combiner(WonderStruct *lhs, WonderStruct *rhs) { 11 | return {lhs->name, rhs->value}; 12 | } 13 | 14 | 15 | WonderStruct* combiner2(WonderStruct *lhs, WonderStruct *rhs) { 16 | // 'out' will only exist within the function's scope, and the 17 | // subsequent print will segfault. 18 | WonderStruct out = {lhs->name, rhs->value}; 19 | return &out; 20 | } 21 | 22 | 23 | WonderStruct* combiner3(WonderStruct *lhs, WonderStruct *rhs) { 24 | WonderStruct *out = new WonderStruct {lhs->name, rhs->value}; 25 | return out; 26 | } 27 | 28 | 29 | void print(WonderStruct *chump) { 30 | std::cout << "Today we review " << chump->name << ".\n" 31 | << "It has been said that " << chump->name 32 | << "'s value as a human being is\n" << chump->value << ".\n\n"; 33 | } 34 | 35 | 36 | int main() { 37 | WonderStruct chump_a = {"Aeva", "something she judges too harshly"}; 38 | WonderStruct chump_b = {"Correct Horse", "dubious at best"}; 39 | WonderStruct mashup = combiner(&chump_a, &chump_b); 40 | print(& mashup); 41 | WonderStruct *mashup2 = combiner3(&chump_b, &chump_a); 42 | print(mashup2); 43 | delete mashup2; 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | This repository contains my notes on learning C++11 as well as 2 | reviewing a number of topics in the core language that differ the most 3 | from other programming languages. 4 | 5 | 6 | If you learned C++ a long time ago and want to get back up to speed 7 | again, or have simply been working in the land of dynamic languages 8 | for the last few years and want to get back to basics, this repository 9 | might be useful to help jog your memory. 10 | 11 | 12 | --> You can find my notes in the file titled "[[learning_cpp.org][learning_cpp.org"]]. <-- 13 | 14 | 15 | The various topic directories contain simple source files that 16 | demonstrate valid syntax for various features in isolation. If they 17 | need any special compiler flags, they will be noted in a comment in 18 | the source file. 19 | 20 | 21 | Please keep in mind a few things in mind: 22 | 23 | - This repository is *not* an in-depth language tutorial. 24 | 25 | - This repository is *not* intended to be an example of my abilities 26 | as a programmer. 27 | 28 | - In lieu of it being stated elsewhere, the code examples are [[http://creativecommons.org/publicdomain/zero/1.0/][public 29 | domain]], and the non-code parts of this repository (eg the org-mode 30 | files learning_cpp.org and readme.org) are made available to you 31 | under the terms of the [[http://creativecommons.org/licenses/by-sa/4.0/][Creative Commons Attribution-ShareAlike 4.0]] 32 | license. 33 | -------------------------------------------------------------------------------- /examples/lambdas/lambdas.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | /* 8 | * To build: 9 | * 10 | * > g++ -std=c++11 lambdas.cpp 11 | * 12 | */ 13 | 14 | 15 | using std::shared_ptr; 16 | using std::deque; 17 | using std::cout; 18 | 19 | 20 | class Thing { 21 | public: 22 | int x = 0; 23 | int y = 0; 24 | Thing(int _x, int _y) { 25 | x = _x; 26 | y = _y; 27 | } 28 | }; 29 | 30 | 31 | int main() { 32 | auto container = deque(); 33 | container.push_back(Thing(10, 10)); 34 | container.push_back(Thing(10, 20)); 35 | container.push_back(Thing(20, 10)); 36 | container.push_back(Thing(20, 20)); 37 | 38 | auto ands = shared_ptr>(new deque()); 39 | auto nots = shared_ptr>(new deque()); 40 | 41 | auto closure = [ands, nots](Thing ct) { 42 | if (ct.x == ct.y) { 43 | (*ands).push_back(ct); 44 | } 45 | else { 46 | (*nots).push_back(ct); 47 | } 48 | return; 49 | }; 50 | 51 | std::for_each(container.begin(), container.end(), closure); 52 | 53 | 54 | cout << "Things where x == y:\n"; 55 | for (auto item : *ands) { 56 | cout << " - x: " << item.x << ", y: " << item.y << "\n"; 57 | } 58 | 59 | 60 | cout << "\nThings where x != y:\n"; 61 | for (auto item : *nots) { 62 | cout << " - x: " << item.x << ", y: " << item.y << "\n"; 63 | } 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /examples/rvalue_refs/basecode.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | std::string get_name() { 5 | return "Aeva"; 6 | } 7 | 8 | 9 | std::string get_name2() { 10 | return std::string("Aeva"); 11 | } 12 | 13 | 14 | std::string get_name3() { 15 | auto name = std::string("Aeva"); 16 | return name; 17 | } 18 | 19 | 20 | template T detect(T& name) { 21 | std::cout << "---> L\n\n"; 22 | return name; 23 | } 24 | 25 | 26 | template T detect(T&& name) { 27 | std::cout << "---> R\n\n"; 28 | return name; 29 | } 30 | 31 | 32 | int main () { 33 | std::cout << "Does get_name() return an l-value or an r-value?\n"; 34 | auto tmp = detect(get_name()); 35 | 36 | std::cout << "Does get_name2() return an l-value or an r-value?\n"; 37 | tmp = detect(get_name2()); 38 | 39 | std::cout << "Does get_name3() return an l-value or an r-value?\n"; 40 | tmp = detect(get_name3()); 41 | 42 | std::cout << "Is the var 'tmp' an l-value or an r-value?\n"; 43 | tmp = detect(tmp); 44 | 45 | std::cout << "What would detect(detect(tmp)) do?\n"; 46 | tmp = detect(detect(tmp)); 47 | 48 | std::cout << "What would detect(23) do?\n"; 49 | tmp = detect(23); 50 | 51 | 52 | std::string copied = get_name(); 53 | std::cout << " copied: " << copied << " " << &copied << "\n"; 54 | 55 | std::string&& moved = get_name(); 56 | std::cout << " moved 1: " << moved << " " << &moved << "\n"; 57 | 58 | moved = get_name(); 59 | std::cout << " moved 2: " << moved << " " << &moved << "\n"; 60 | 61 | moved = std::move(moved); 62 | std::cout << "std::move: " << moved << " " << &moved << "\n"; 63 | 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /examples/async/locking_rewrite.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* 10 | * To build: 11 | * 12 | * > g++ -std=c++11 -pthread locking_rewrite.cpp 13 | * 14 | */ 15 | 16 | std::mutex mtx; 17 | const uint width = 79; 18 | const uint lines = width/2; 19 | auto buffer = std::array(); 20 | 21 | 22 | void clear () { 23 | for (int line=0; line= lines) { 41 | break; 42 | } 43 | for (uint col=x; col= width) { 45 | break; 46 | } 47 | buffer[row][col] = stamp; 48 | } 49 | } 50 | mtx.unlock(); 51 | }; 52 | 53 | 54 | void draw_sqr(char stamp, uint x, uint y, uint w) { 55 | uint h = w * 0.55; 56 | draw_rect(stamp, x, y, w, h); 57 | }; 58 | 59 | 60 | std::deque > futures; 61 | template void async(T call) { 62 | futures.push_back(std::async(std::launch::async, call)); 63 | } 64 | 65 | 66 | int main () { 67 | clear(); 68 | 69 | const char* gradient = "#o8%#"; 70 | 71 | for (int i=0; i<5; i+=1) { 72 | char brush = gradient[i]; 73 | uint x = (i) * 2 + 1; 74 | uint y = (i) * 3 + 1; 75 | uint w = (i+1) * 10; 76 | async(std::bind(draw_sqr, brush, x, y, w)); 77 | } 78 | 79 | for (auto& future : futures) { 80 | future.wait(); 81 | } 82 | 83 | print(); 84 | }; 85 | -------------------------------------------------------------------------------- /examples/async/locking.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* 10 | * To build: 11 | * 12 | * > g++ -std=c++11 -pthread locking.cpp 13 | * 14 | */ 15 | 16 | std::mutex mtx; 17 | const uint width = 79; 18 | const uint lines = width/2; 19 | auto buffer = std::array(); 20 | 21 | 22 | void clear () { 23 | for (int line=0; line= lines) { 41 | break; 42 | } 43 | for (uint col=x; col= width) { 45 | break; 46 | } 47 | buffer[row][col] = stamp; 48 | } 49 | } 50 | mtx.unlock(); 51 | }; 52 | 53 | 54 | void draw_sqr(char stamp, uint x, uint y, uint w) { 55 | uint h = w * 0.55; 56 | draw_rect(stamp, x, y, w, h); 57 | }; 58 | 59 | 60 | std::queue > futures; 61 | template void async(T call) { 62 | futures.push(std::async(std::launch::async, call)); 63 | } 64 | 65 | 66 | int main () { 67 | clear(); 68 | 69 | const char* gradient = "#o8%#"; 70 | 71 | for (int i=0; i<5; i+=1) { 72 | char brush = gradient[i]; 73 | uint x = (i) * 2 + 1; 74 | uint y = (i) * 3 + 1; 75 | uint w = (i+1) * 10; 76 | async(std::bind(draw_sqr, brush, x, y, w)); 77 | } 78 | 79 | while (!futures.empty()) { 80 | futures.front().wait(); 81 | futures.pop(); 82 | } 83 | 84 | print(); 85 | }; 86 | -------------------------------------------------------------------------------- /examples/smart_ptr/unique_ptr.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | /* 7 | * To build: 8 | * 9 | * > g++ -std=c++11 unique_ptr.cpp 10 | * 11 | */ 12 | 13 | 14 | using std::unique_ptr; 15 | 16 | 17 | class Resource { 18 | public: 19 | Resource() { 20 | std::cout << "misc resource initialized\n"; 21 | } 22 | ~Resource() { 23 | std::cout << "misc resource deleted\n"; 24 | } 25 | }; 26 | 27 | 28 | class Thingy { 29 | public: 30 | Thingy() : res(new Resource) { 31 | std::cout << "thingy initialized\n"; 32 | } 33 | ~Thingy() { 34 | std::cout << "thingy deleted\n"; 35 | } 36 | private: 37 | unique_ptr res; 38 | }; 39 | 40 | 41 | int main() { 42 | // --- will refuse to compile --- 43 | // auto foo = unique_ptr("Hello world!"); 44 | 45 | auto foo = unique_ptr(new (const char*)("Hello world!")); 46 | auto bar = unique_ptr(new int(10)); 47 | // std::cout << foo << "\n"; // <----- will refuse to compile 48 | // std::cout << bar << "\n"; // <----- will refuse to compile 49 | std::cout << *foo << "\n"; 50 | std::cout << *bar << "\n"; 51 | 52 | auto baz = unique_ptr(new std::string("Is this thing on?")); 53 | std::cout << *baz << "\n"; 54 | 55 | baz.reset(); 56 | // std::cout << *baz << "\n"; // reading will segfault 57 | // baz = new std::string("hello"); // cant just reassign 58 | baz.reset(new std::string("test")); 59 | 60 | decltype(baz) eep = unique_ptr(); 61 | eep = std::move(baz); 62 | 63 | // std::cout << *baz << "\n"; // will segfault 64 | std::cout << *eep << "\n"; 65 | 66 | 67 | // It appears that you can however steal the reference from a 68 | // unique_ptr and assign it to a raw pointer. 69 | std::string* sneaky = &*eep; 70 | (*sneaky)[0] = '-'; 71 | std::cout << *eep << "\n"; 72 | std::cout << *sneaky << "\n"; 73 | 74 | 75 | auto thing = unique_ptr(new Thingy); 76 | thing.reset(); // <-- destructors will be called regardless on program close 77 | 78 | return 0; 79 | } 80 | -------------------------------------------------------------------------------- /learning_cpp.org: -------------------------------------------------------------------------------- 1 | 2 | * study log day 1: learning C++11 3 | ** rvalues and lvalues 4 | An l-value is anything that you can get the address of, and is always 5 | the "left hand side" of an assignment operator. 6 | 7 | The parameters of a function are always l-values, as is the LHS of an 8 | assignment operator. Function calls that return a reference are also 9 | l-values. 10 | 11 | 12 | An expression is an r-value if it returns a temporary object, eg 13 | anything that doesn't have an explicit address. So literals like 23 14 | and "c strings" are r-values. If a function returns an object (not a 15 | reference), the return value is also an r-value. 16 | 17 | 18 | [[http://www.cprogramming.com/c%2B%2B11/rvalue-references-and-move-semantics-in-c%2B%2B11.html][This tutorial on move semantics]] has a nice explanation of l-values and 19 | r-values. 20 | 21 | ** type inference rules, function templates, and 'auto' 22 | You can apparently (this is not new) do something like this: 23 | 24 | #+BEGIN_SRC c++ 25 | template void foo(T param) { 26 | std::cout << param << "\n"; 27 | } 28 | 29 | foo(20); 30 | foo(20.3); 31 | foo("hello world!"); 32 | 33 | #+END_SRC 34 | 35 | It turns out that the rules for type inference used for function 36 | templates also all apply to the *auto* keyword. The one exception of 37 | this is {1,2,3} syntax, which auto recognizes as some variety of 38 | array, but templates will barf over. 39 | 40 | ** arrays are a distinct type from pointers 41 | Interestingly, arrays are a type that is defferent from a pointer, but 42 | will often be downgraded to a pointer. 43 | 44 | An array's length is also part of its type - so arrays of two 45 | different lengths but contain the same item type are not necessarily 46 | compatible with one another, though they wil degrade to the same 47 | pointer type. 48 | 49 | 50 | Array notation in a function parameter is, however, a pointer, unless 51 | it is written out so as to reference an array (in which length will be 52 | needed too). 53 | 54 | 55 | When an array degrades to a pointer, it is a pointer to the first item 56 | in the array. 57 | 58 | ** decltype and type deduction 59 | "Given a name or an expression, decltype tells you the name’s or the 60 | expression’s type." 61 | 62 | This seems to be used mostly for deriving a return type from an 63 | expression. 64 | 65 | 66 | In this example, 'auto' indicates that the return value is defined 67 | after the params, denoted by the '->' symbol. 68 | 69 | #+BEGIN_SRC c++ 70 | template 71 | auto 72 | authAndAccess(Container&& c, Index i) 73 | -> decltype(std::forward(c)[i]) 74 | { 75 | authenticateUser(); 76 | return std::forward(c)[i]; 77 | } 78 | #+END_SRC 79 | 80 | 81 | One of the many gotchas for decltype is that if decltype(x) will be 82 | 'int' (assuming x is an int) but decltype((x)) will be int&. 83 | 84 | 85 | --- 86 | 87 | Decltype is something that is never actually evaluated. Here is 88 | another example of its use: 89 | 90 | #+BEGIN_SRC c++ 91 | 92 | auto floob = "This is a test!"; 93 | decltype(floob) thingy = "hello world"; 94 | 95 | #+END_SRC 96 | ** auto 97 | Auto can be tripped up by proxy classes. For example 98 | std::vector[] returns a proxy class instead of a bool. If the 99 | return result from a function is being indexed like this, a pointer to 100 | the return result might be generated before the return result is GC'd! 101 | Or it might behave as expected. 102 | 103 | Otherwise, auto is a miracle that makes C++'s proliferation of types 104 | to be bearable to deal with. 105 | 106 | - 107 | 108 | So if something is behaving wrong, it might be worthwhile to cast to 109 | what you think should be when assigning to an auto. 110 | 111 | ** aliases vs TYPEDEF 112 | Aliases make function pointers more clear: 113 | 114 | #+BEGIN_SRC c++ 115 | typedef void (*function_pointer)(int, const std::string&); 116 | using function_pointer = void (*)(int, const std::string&); 117 | #+END_SRC 118 | 119 | Basically, typedef is a keyword you put before a standard variable 120 | declaration, whereas 'using' is more like the assignment syntax. 121 | 122 | ** nullptr vs NULL vs 0 123 | When you set a pointer's address to 0, the compiler will coerce this 124 | to mean it is now a null pointer rather than pointing to that specific 125 | address. 126 | 127 | The definition of "NULL" is implementation dependent, and is often 0. 128 | 129 | "nullptr" by contrast is the null pointer itself, and so is more 130 | semantically correct to use. 131 | 132 | 133 | * study log day 2: review of the basics, more C++11 134 | ** pointers and references review 135 | #+BEGIN_SRC c++ 136 | int a = 10; 137 | int& b = a; 138 | b += 10; 139 | std::cout << " 'a' is " << a << "\n"; 140 | std::cout << " 'b' is " << b << "\n"; 141 | 142 | int c = 10; 143 | int* d = &c; 144 | std::cout << " 'c' is " << c << "\n"; 145 | std::cout << "'&c' is " << &c << "\n"; 146 | std::cout << " 'd' is " << d << "\n"; 147 | std::cout << "'*d' is " << *d << "\n"; 148 | std::cout << "'&d' is " << &d << "\n"; 149 | #+END_SRC 150 | 151 | 152 | So, the simple rule here is: 153 | 154 | - &foo gives the address of foo 155 | - foo gives the value of foo 156 | - *foo gives the value of what foo points to 157 | 158 | ...and, pointers are normal variables that store memory addresses. 159 | 160 | 161 | -------------- 162 | 163 | Declaring pointers takes this form: 164 | 165 | - type * foo; 166 | 167 | Whitespace is optional, so "type*foo", "type* foo" and "type *foo" are 168 | all valid. 169 | 170 | 171 | -------------- 172 | 173 | Declaring reference variables takes this form: 174 | 175 | - type & foo; 176 | 177 | Curiously in this case, the only acceptable values are l-values, and 178 | you don't provide the address. I think this basically means that you 179 | pass an variable that presumably has an address associated to it, and 180 | the compiler is able to infer so without any special syntax. 181 | ** casting semantics 182 | These two casting conventions will yield an identical output for 183 | value-based casting: 184 | #+BEGIN_SRC c++ 185 | double some_num = 1.4; 186 | // C style 187 | int a = (int) some_num; 188 | // C++ style 189 | int b = static_cast (some_num); 190 | #+END_SRC 191 | 192 | C++ also includes 'dynamic_cast', 'reinterpret_cast', and 193 | 'const_cast', which operate entirely on pointers and are used for some 194 | varieties of polymorphism. 195 | 196 | The static_cast mechanism can be used on pointers, too. For example: 197 | #+BEGIN_SRC c++ 198 | SomeClass* foo = static_cast(malloc(sizeof(SomeClass))); 199 | #+END_SRC 200 | 201 | ** malloc and free 202 | The dreaded malloc() takes a number of bytes and returns a void pointer 203 | to the uninitialized newly allocated memory. 204 | 205 | Calling free() on the pointer returned by malloc frees the memory. 206 | 207 | ** function templates 208 | OMG these things are great! They work with very similar rules to 209 | auto, differing only in what the array/universal initialization syntax 210 | denotes. As such, they are a great way to sort of force duck typing 211 | onto C++. 212 | 213 | #+BEGIN_SRC c++ 214 | #include 215 | 216 | template void foo(T param) { 217 | std::cout << param << "\n"; 218 | } 219 | 220 | int main() { 221 | 222 | foo(20); 223 | foo(20.3); 224 | foo("hello world!"); 225 | 226 | return 0; 227 | } 228 | #+END_SRC 229 | ** const, volitile, mutable 230 | Const means that the memory for the object shouldn't be modified. 231 | 232 | Volatile means that the compiler shouldn't try to optimize out a 233 | thing, supposedly useful for signal handlers. 234 | 235 | Mutable applies to class members and means that the member does not 236 | affect the externally visible state of the class. This is used for 237 | things like memos, lazy evaluation, mutexes, etc. This also allows 238 | you to make members of a const class modifiable. 239 | 240 | Mutable and const are mutually exclusive. Const and Volatile can be 241 | combined. 242 | 243 | ** public, protected, private, and friends 244 | Public works as you'd expect. 245 | 246 | Protected means children can also see it. 247 | 248 | Private is totally private to the base class. 249 | 250 | A class that is designated as a "Friend" can view all three. A class 251 | defines who their friends are, not vice versa. 252 | 253 | ** deleted functions and using auto& instead of auto 254 | 255 | Consider the following broken code and compiler error: 256 | 257 | #+BEGIN_SRC c++ 258 | for (auto future : futures) { 259 | future.wait(); 260 | } 261 | #+END_SRC 262 | 263 | #+BEGIN_SRC txt 264 | locking_rewrite.cpp: In function ‘int main()’: 265 | locking_rewrite.cpp:80:22: error: use of deleted function ‘std::future::future(const std::future&)’ 266 | for (auto future : futures) { 267 | ^ 268 | In file included from locking_rewrite.cpp:2:0: 269 | /usr/include/c++/5.3.1/future:832:7: note: declared here 270 | future(const future&) = delete; 271 | #+END_SRC 272 | 273 | 274 | 275 | The important part here is the deleted function: 276 | 277 | - "std::future::future(const std::future&)" 278 | 279 | What this means is that std::future objects can't be copied. As a 280 | result, if you try to return (by value) one from a function, it'll 281 | call the copy constructor and fail. 282 | 283 | To fix this, you just change the code to read: 284 | #+BEGIN_SRC c++ 285 | for (auto& future : futures) { 286 | future.wait(); 287 | } 288 | #+END_SRC 289 | 290 | ...and everything will work fine. 291 | 292 | ** async calls and std::bind 293 | Async calls return a 'future' type, which you can call a number of 294 | variations of 'get', 'wait' etc on. This is really nice for job-based 295 | concurrency, as you can just define your thread as having input and 296 | producing some output, and not worry about using queues etc to 297 | communicate. 298 | 299 | #+BEGIN_SRC c++ 300 | auto future = std::async(std::launch::async, f, arg1, arg2...) 301 | #+END_SRC 302 | 303 | 304 | This however screams "20 type pileup", not to mention it is annoying 305 | to have to tell it that you absolutely do want threading to be 306 | employed. 307 | 308 | - 309 | 310 | std::bind is a template function that is handy because it encapsulates 311 | the arguments and is good for things like timeouts. This lends itself 312 | well with the async stuff, because you can write a small boilerplate 313 | method like so: 314 | 315 | #+BEGIN_SRC c++ 316 | std::dequeue > futures; 317 | template void async(T call) { 318 | futures.push(std::async(std::launch::async, call)); 319 | } 320 | #+END_SRC 321 | 322 | ... and call it like so: 323 | 324 | #+BEGIN_SRC c++ 325 | async(std::bind(some_method, some_arg, some_arg)); 326 | #+END_SRC 327 | 328 | 329 | Of course, this could also just be re-written to use multiple argument 330 | syntax and eschew the std::bind; but I like this because it makes the 331 | boiler plate simple. 332 | 333 | ** default arguments 334 | Super easy: 335 | #+BEGIN_SRC c++ 336 | template void print (T value, const char* label = "result") { 337 | std::cout << label << ": " << value << "\n"; 338 | } 339 | 340 | // .. 341 | 342 | print(10); 343 | print("hello"); 344 | print("hello", "some string"); 345 | #+END_SRC 346 | 347 | ** range based for-loops 348 | Ranged based for loops provide a nice syntax for looping over 349 | iterators. 350 | 351 | #+BEGIN_SRC c++ 352 | auto nums = std::vector; 353 | for (int i=0; i<10; i+=1) { 354 | nums.push_back(i); 355 | } 356 | 357 | // ranged base loop 358 | for (int i : nums) { 359 | std::cout << i << "\n"; 360 | } 361 | #+END_SRC 362 | 363 | ** rvalue references 364 | An r-value reference is a new reference type that lets you store a 365 | temporary object. An example: 366 | 367 | #+BEGIN_SRC c++ 368 | string get_name() { 369 | return "Aeva"; 370 | } 371 | 372 | string copied = get_name(); 373 | string&& moved_1 = get_name(); 374 | string&& moved_2 = get_name(); 375 | #+END_SRC 376 | 377 | In this example, both 'moved' vars will have the same final address. 378 | The rvalue reference syntax shown above will produce lvalues 'moved_1' 379 | and 'moved_2', which will both have the same memory address. The 380 | lvalue 'copied' will however have its own unique memory address. 381 | 382 | --- 383 | 384 | Now, what happens when && refs are used as function arguments? 385 | 386 | #+BEGIN_SRC c++ 387 | template T detect(T& name) { 388 | std::cout << "---> L\n\n"; 389 | return name; 390 | } 391 | template T detect(T&& name) { 392 | std::cout << "---> R\n\n"; 393 | return name; 394 | } 395 | 396 | 397 | auto foo = detect("hello"); // r value 398 | detect(foo); // l value 399 | detect(detect(foo)); // l value, then r value 400 | #+END_SRC 401 | 402 | wheeeeee! 403 | 404 | ** move constructors and std::move 405 | A class can have a copy constructor, which is a method without a 406 | return type that takes this form: 407 | 408 | ClassName (const ClassName& other) { ... } 409 | 410 | 411 | Note that "const ClassName& arg" will catch both l-values and r-values 412 | unless a rvalue reference constructor also exists, such as: 413 | 414 | 415 | We can also define a move constructor now, 416 | 417 | ClassName (ClassName&& other) { ... } 418 | 419 | 420 | Where the copy constructor is responsible for manually making a deep 421 | copy of another instance of the class, a move constructor simply does 422 | this: 423 | 424 | 1. copy over primitives (eg ints) 425 | 426 | 2. for pointers, create a new pointer of the same type, assign the 427 | new one the value of the old one, and zero out the old pointer. 428 | 429 | 430 | The idea here being that we really just want to reuse existing memory, 431 | and we want the new things to be "owned" by our class. 432 | 433 | 434 | --- std::move --- 435 | 436 | Sometimes however you need to chain move constructors. You can't 437 | simply call the move constructor for the child object because you only 438 | have an l-value handle for it. 439 | 440 | What std::move does is you pass in an l-value, and get an r-value in 441 | return. 442 | 443 | ** class constructor member initialization list 444 | It is a syntax for initializing non-static member variables of the 445 | class. The member initialization list is executed before the 446 | constructor is. 447 | 448 | 449 | A contrived example: 450 | #+BEGIN_SRC c++ 451 | class Floob 452 | { 453 | public: 454 | Floob(int n) 455 | : a_vars( new int[n] ) 456 | , b_vars( new double[n] ) 457 | {} 458 | ~Floob() 459 | { 460 | delete [] a_vars; 461 | delete [] b_vars; 462 | } 463 | private: 464 | int *a_vars; 465 | double *b_vars; 466 | } 467 | #+END_SRC 468 | 469 | 470 | * study log day 3: more review, more C++11 471 | ** unique_ptr 472 | Smart pointers are defined under the header . 473 | 474 | A unique pointer is a container for a raw pointer, which prevents 475 | copying its contained pointer. 476 | 477 | The copy constructor and assignment constructers on the class are 478 | deleted, so one can only use the move constructor to hand unique_ptrs 479 | around. 480 | 481 | --- 482 | 483 | It seems that a unique pointer can Only Very Definatively be set via 484 | its constructor, the reset command, or the move constructor. 485 | 486 | uptr = std::move(other_uptr); works 487 | uptr = std::move(some_lvalue); refuses to compile 488 | 489 | --- 490 | 491 | The point of these is a pointer type that can only ever live in one 492 | scope at a time, and thus the memory can be automatically freed when 493 | the scope exits. 494 | 495 | ** shared_ptr 496 | Shared pointers are another container type for wrapping raw pointers. 497 | Unlike unique_ptr, it may be copied, but it employes reference 498 | counting. When the last copy of a shared pointer is deleted, only 499 | then is the encapsulated object reference counted. 500 | 501 | 502 | Shared pointers are *awesome*. 503 | 504 | ** weak_ptr 505 | These are compatible with shared pointers, but do not effect reference 506 | counting, and therefor may be used to prevent reference loops. 507 | 508 | ** lambdas 509 | Lambda expressions allow you to construct closure objects (and thus 510 | closure classes for those objects). These are probably most 511 | powerfully demonstrated with the standard library's 512 | functions, providing the joy of basic functional programming through a 513 | very fraught implementation. 514 | 515 | The syntax is simple: 516 | 517 | #+BEGIN_SRC c++ 518 | auto closure = [capture](args) {body}; 519 | #+END_SRC 520 | 521 | Closure objects are able to access some of the local scope that they 522 | were defined in via the capture clause. Some permutations: 523 | 524 | #+BEGIN_SRC c++ 525 | // some local scope 526 | int x = 10; 527 | auto y = std::shared_ptr(new int(20)); 528 | 529 | // capture nothing 530 | auto closure_a = [](int arg1) { return arg1; }; 531 | 532 | // capture all local scope by reference 533 | auto closure_b = [&]() { return x+y; }; 534 | 535 | // capture explicit local variables by reference 536 | auto closure_b = [&x, &y]() { return x+y; }; 537 | 538 | // capture all local scope by value 539 | auto closure_b = [=]() { return x+y; }; 540 | 541 | // capture explicit local scope by value 542 | auto closure_b = [x, y]() { return x+y; }; 543 | #+END_SRC 544 | 545 | 546 | You can approximate r-value capturing by using std::bind and the 547 | arguments list: 548 | #+BEGIN_SRC c++ 549 | auto special_snowflake = std::unique_ptr(new int(20)); 550 | auto closure = std::bind([](const std::unique_ptr& floob) {}, 551 | std::move(special_snowflake)); 552 | #+END_SRC 553 | 554 | 555 | The non-const variation requires the 'mutable' keyword: 556 | #+BEGIN_SRC c++ 557 | auto closure = std::bind([](std::unique_ptr& floob) mutable {}, 558 | std::move(special_snowflake)); 559 | #+END_SRC 560 | 561 | 562 | Some final notes on lambdas, 563 | 564 | - They only capture *local scope* - so if you generate one in a 565 | function, you can only access member variables via the function's 566 | 'this' pointer. Which is fraught. 567 | 568 | - They are somewhat preferential to bind in most cases, according to 569 | that book I'm reading right now. 570 | 571 | ** variadic templates, variable arg functions 572 | This is a syntax for variable type arguments for class and function 573 | templates. It looks something like this: 574 | 575 | #+BEGIN_SRC c++ 576 | // variadic class template 577 | template class tuple; 578 | 579 | // variadic function template 580 | template 581 | void printf(const std::string &str_format, Params... parameters); 582 | #+END_SRC 583 | 584 | 585 | To access the arguments list, you'd do so with "args..." in the 586 | function. Wikipedia proclaims that there is no simple way to iterate 587 | over the arguments the list unsurprisingly, though you can pass it on 588 | to a different method easily: 589 | 590 | #+BEGIN_SRC c++ 591 | some_function(args...); 592 | #+END_SRC 593 | 594 | 595 | At a guess, this is mainly used for meta programming. 596 | 597 | ** classes and structs 598 | Classes and structs are literally the same damn thing in C++. The 599 | only difference between the two that I am aware of, is that the 600 | members of structs are public by default whereas the members of 601 | classes are private by default. 602 | 603 | The basic syntax for both looks like this: 604 | #+BEGIN_SRC c++ 605 | class Thingy { 606 | public: 607 | Thingy() : res(new Resource) { 608 | std::cout << "thingy initialized\n"; 609 | } 610 | Thingy(const Thingy&) { 611 | std::cout << "copy constructor was called\n"; 612 | } 613 | Thingy(Thingy&&) { 614 | std::cout << "move constructor was called\n"; 615 | } 616 | ~Thingy() { 617 | std::cout << "thingy deleted\n"; 618 | } 619 | Thingy operator=(const Thingy& rhs); 620 | Thingy &operator[](int arg); 621 | operator int() { return 10; } // type conversion operator 622 | Thingy(int rhs); // constructor for implicit conversion eg "Thingy foo = 10;" 623 | protected: 624 | // pseudo-private namespace that child classes and "friends" can access 625 | private: 626 | // actually private, except to "friends" 627 | unique_ptr res; 628 | }; 629 | #+END_SRC 630 | 631 | ** defining class methods 632 | In more complicated projects or in libraries, a class and all of its 633 | methods are usually declared in a header file separately from its 634 | method implementations. 635 | 636 | The example class in the section above defines its constructor and 637 | destructor methods inline, but then only declares the operator 638 | methods. 639 | 640 | Here is an example of defining your methods separately from their 641 | declaration: 642 | 643 | #+BEGIN_SRC c++ 644 | // This would usually be in some header file. 645 | struct AnotherThing 646 | { 647 | AnotherThing(); 648 | void SpectacularMethod (int beep); 649 | }; 650 | 651 | // The following methods would then be in a cpp file that includes the 652 | // header: 653 | 654 | AnotherThing::AnotherThing() { 655 | std::cout << "AnotherThing's constructor was called.\n"; 656 | } 657 | 658 | AnotherThing::SpectacularMethod (int beep) 659 | { 660 | std::cout << beep << " is a number!\n"; 661 | } 662 | #+END_SRC 663 | 664 | ** inheritance 665 | Basic inheritance looks like so: 666 | #+BEGIN_SRC c++ 667 | class BaseClass { 668 | public: 669 | // ... 670 | }; 671 | 672 | class ChildClass : public BaseClass { 673 | public: 674 | // ... 675 | }; 676 | #+END_SRC 677 | 678 | An instance of a child class is considered to have a "sub-object" of 679 | the type of it's parent class. So that means the parent constructor 680 | is always called, like so: 681 | 682 | 1. parent constructs 683 | 2. child constructs 684 | 685 | Deletion happens in reverse order, so: 686 | 687 | 1. child destructs 688 | 2. parent destructs 689 | 690 | 691 | By default, the compiler assumes you want the vanilla constructor. To 692 | specify otherwise, you would define a child class like so: 693 | 694 | #+BEGIN_SRC c++ 695 | class ChildClass : public BaseClass { 696 | public: 697 | ChildClass() : BaseClass(10) { 698 | cout << "derived class initializer called\n"; 699 | }; 700 | ~ChildClass() { 701 | cout << "derived class destructor called\n"; 702 | }; 703 | }; 704 | #+END_SRC 705 | 706 | ** polymorphism 707 | If you have a pointer to BaseClass, but it points to DerivedClass, it 708 | will only be able to call methods that were implemented on BaseClass. 709 | 710 | Unless you marked a method on BaseClass as "virtual", in which case it 711 | will call DerivedClass's implementation of the method. 712 | 713 | The reason for this is static_binding is used at compile time (early 714 | binding), unless the keyword is present, in which case dynamic binding 715 | (late binding) is used instead to determine what function to call. 716 | 717 | A good rule is that if you are going to have a virtual method on 718 | BaseClass, you should mark the DerivedClass version of that method 719 | with "override". This will prevent the program from compiling unless 720 | there is actually the desired corresponding virtual method. 721 | 722 | - 723 | 724 | A base class can be left as an "abstract base class" by defining one 725 | or more virtual methods as not having a body. These are called "pure 726 | virtual" functions, since they can't be statically bound. Pure 727 | virtual functions are defined like so: 728 | 729 | #+BEGIN_SRC c++ 730 | struct AbstractBaseClassExample 731 | { 732 | virtual void OverrideMe() = 0; 733 | }; 734 | #+END_SRC 735 | 736 | A derrived class might then look something like this: 737 | #+BEGIN_SRC c++ 738 | struct DerivedClass : public AbstractBaseClassExample 739 | { 740 | void OverrideMe() override; 741 | }; 742 | #+END_SRC 743 | 744 | ** multiple inheritance 745 | You can have a class derive from multiple parent classes in C++ :( 746 | 747 | In these cases you will also likely need to use the 'explicit parent 748 | constructor' syntax. 749 | 750 | #+BEGIN_SRC c++ 751 | class ChildClass : public BaseClassA, public BaseClassB { 752 | public: 753 | ChildClass() : BaseClassA(10), BaseClassB("meep") { 754 | }; 755 | }; 756 | #+END_SRC 757 | 758 | 759 | In the odd case that you have a hierarchy like so: 760 | 761 | a 762 | b->a 763 | c->a 764 | d->b,c 765 | 766 | And are not using poly morphism, and call upon an instance of 'd' a 767 | method that is defined on 'a', there is the question as to which 768 | version of 'a' should be used - the one that belongs to 'b' or the one 769 | that belongs to 'c'? 770 | 771 | The solution here is to define 'b' and 'c' like so: 772 | 773 | #+BEGIN_SRC c++ 774 | class ExampleB : public virtual ExampleA {}; 775 | #+END_SRC 776 | 777 | And then for an instance of class 'd', there will only be one created 778 | instance of class 'a' instead of two. 779 | 780 | ** c-strings vs string objects 781 | C-strings are \0 terminated arrays of bytes, of which the compiler has 782 | syntatic sugar for recognizing and composing ascii encoded bytes, and 783 | a number of helper functions exist. 784 | 785 | C-strings are often of the type (const char*). 786 | 787 | A std::string is an object that encapsulates these to make things a 788 | lot cleaner. You instance them by passing a c-string into the 789 | constructor. The class defines a number of member functions to 790 | replace the functionality standard library for manipulating c-strings. 791 | 792 | ** exceptions 793 | Similar to JS, but the type system applies, allowing for multiple 794 | catch statements. 795 | 796 | #+BEGIN_SRC c++ 797 | try { 798 | throw ("error"); 799 | } 800 | catch (const char* err) { 801 | // what actually gets called in this case 802 | } 803 | catch (CustomErrorClass foo) { 804 | // what might have been called instead 805 | } 806 | #+END_SRC 807 | 808 | If an exception is not called, you'll get something in the consoles 809 | saying "Aborted (core dumped)" (or whatever platform-specific crash 810 | handler). 811 | 812 | ** debugging (gcc) 813 | You can add debugging symbols by adding '-g' to the compiler. This 814 | will pick the native format for the platform. '-ggdb' produces 815 | debugging symbols for gdb, which seems to work fine with nemiver. 816 | 817 | > g++ -std=c++11 -ggdb sourcefile.cpp 818 | > nemiver a.out 819 | 820 | ** custom namespaces 821 | Simple! 822 | 823 | #+BEGIN_SRC c++ 824 | namespace FancyNamespace { 825 | class blorf { 826 | }; 827 | 828 | void some_function () { 829 | }; 830 | } 831 | #+END_SRC 832 | 833 | ** atomic types 834 | The std::atomic template is used to produce the [[http://en.cppreference.com/w/cpp/atomic/atomic][atomic types]] from 835 | "trivially copyable" types like int. 836 | 837 | Atomic types are supposedly types where it is "impossible to observe" 838 | the memory of the object in a halfway complete state. This could mean 839 | that for some types, thier read/write ops are single instruction. It 840 | could also mean that a lock is employed behind the scenes. 841 | 842 | - 843 | 844 | Atomic types are praised for being "lockless", but are still 845 | effectively a form of synchronization primative :P. 846 | 847 | As best I can tell, you would use these when an atomic type is the 848 | only thing you want to share between threads. 849 | 850 | Atomics are non-movable and non-copiable. 851 | 852 | ** std::thread 853 | [[http://en.cppreference.com/w/cpp/thread/thread/thread][Thread objects]], when constructed without args do not represent any 854 | threads. A move constructor is provided, and you can also construct a 855 | thread with a function and some arguments. There is no copy 856 | constructor. 857 | 858 | In the event that a function and arguments was passed without error, a 859 | new thread will be created, and the thread will branch to the provided 860 | function (the return value is ignored). The returned thread object 861 | will represent the newly created thread. 862 | 863 | There can only be one thread object to a thread. 864 | 865 | Predictably there is a mechanism for joining and one for detaching the 866 | thread. 867 | 868 | Where this differs from std::async - async is a high level interface 869 | that can create a std::thread object, but does not necessarily need to 870 | (see async policy). Async returns a future, and the function passed 871 | into the async statement is assumed to return at some point. 872 | 873 | 874 | * study log day 4: review --- oh and regexes! 875 | Things that didn't quite stick. 876 | ** volatile 877 | This keyword is to prevent the compiler from optimizing out a 878 | variable. 879 | 880 | ** mutable 881 | Mutable allows you to declare some members of a const class to be 882 | mutable, thus allowing you to change them. This is useful for things 883 | like memoization, lazy eval, mutexes, and so on. 884 | 885 | Also, with lambdas, capture-by-value'd vars are const by default. To 886 | change that, 887 | 888 | #+BEGIN_SRC c++ 889 | int foo = 10; 890 | auto closure = [=]() mutable { foo = 20; }; 891 | #+END_SRC 892 | 893 | ** rvalue references 894 | These references seem to mostly be useful as function arguments to use 895 | different behavior for handling r-value params. An example of this is 896 | a move constructor. 897 | 898 | ** default args 899 | This is fairly easy - works like it does in python. 900 | 901 | #+BEGIN_SRC c++ 902 | int example_function(int arg1, int arg2 = -1) { 903 | return arg1 + arg2; 904 | } 905 | #+END_SRC 906 | ** regexes! 907 | Apparently C+11 [[https://solarianprogrammer.com/2011/10/12/cpp-11-regex-tutorial/][has regexes]] and nobody told me! Well then. 908 | 909 | C++11 uses javascript's gramar, though it seems to lack the same 910 | flags. 911 | 912 | Here is a simple usage example, wherein the regex finds all of the 913 | numbers in a given string. 914 | 915 | #+BEGIN_SRC c++ 916 | #include 917 | #include 918 | #include 919 | 920 | int main () { 921 | auto raw = std::string("vertex -1.624555 -4.999952 -8.506543"); 922 | std::smatch match_itr; 923 | std::regex pattern("[0-9-.]+"); 924 | while(std::regex_search(raw, match_itr, pattern)) { 925 | std::cout << match_itr[0] << "\n"; 926 | raw = match_itr.suffix().str(); 927 | } 928 | return 0; 929 | } 930 | #+END_SRC 931 | --------------------------------------------------------------------------------