Skip to content

Latest commit

 

History

History
242 lines (181 loc) · 7.95 KB

File metadata and controls

242 lines (181 loc) · 7.95 KB

My C++ notes

This document captures basic concepts and features used in modern C++. I tend to learn better by example, so I will briefly describe and concept/feature and then give example(s) to show how it is used.

Note: This document is in progress.

Setup

These notes will be in literate programming style. All code will be compiled and executed using the clang compiler. Specifically, this command was used. In some examples, I also plan to do benchmarking using this library.

Resources

Basic concepts

Zoomed out view of the language concepts: https://en.cppreference.com/w/cpp/language/basic_concepts

Quote:

” A C++ program is a sequence of text files (typically header and source files) that contain declarations. They undergo translation to become an executable program, which is executed when the C++ implementation calls its main function.

Certain words in a C++ program have special meaning, and these are known as keywords. Others can be used as identifiers. Comments are ignored during translation. Certain characters in the program have to be represented with escape sequences.

The entities of a C++ program are values, objects, references, structured bindings (since C++17), functions, enumerators, types, class members, templates, template specializations, namespaces, and parameter packs. Preprocessor macros are not C++ entities.

Declarations may introduce entities, associate them with names and define their properties. The declarations that define all properties required to use an entity are definitions. A program must contain only one definition of any non-inline function or variable that is odr-used.

Definitions of functions usually include sequences of statements, some of which include expressions, which specify the computations to be performed by the program.

Names encountered in a program are associated with the declarations that introduced them using name lookup. Each name is only valid within a part of the program called its scope. Some names have linkage which makes them refer to the same entities when they appear in different scopes or translation units.

Each object, reference, function, expression in C++ is associated with a type, which may be fundamental, compound, or user-defined, complete or incomplete, etc.

Declared objects and declared references that are not non-static data members are variables. ”

Each object has a life cycle.

We’ll dig deeper into some of these below.

Types

See: https://en.cppreference.com/w/cpp/language/type

“Objects, references, functions including function template specializations, and expressions have a property called type, which both restricts the operations that are permitted for those entities and provides semantic meaning to the otherwise generic sequences of bits.”

Basic

Aka fundamental or built-in. https://en.cppreference.com/w/cpp/language/type.

Container / STL

https://github.com/spraza/tut-notes/blob/master/cpp-stl.org

Auto

Move semantics

First, we need to understand the difference between lvalues and rvalues:

std::move doc: https://en.cppreference.com/w/cpp/utility/move. Basically, std::move(X) is exactly equivalent to static_cast<T&&>(X) where T is the deduced type of expression X. T&& is the rvalue reference type.

rvalue reference generally means “no address”. so when a function or template parameter is typed as rvalue reference, then we know that the thing being passed can not be accessed outside – so i am the only one who knows about it and so i can optimize my code based on it. one example: reuse data in rvalue reference instead of making another copy (cz then we’ll have 2 copies and the one passed in will be wasted later).

Let’s see how using move and rvalue reference to do shallow copies instead of deep copy helps:

 #include <iostream>
 #include <memory>
 #include <chrono>
 #include <type_traits>

 using namespace std;

 template<class T> class MyVector {
 public:
   MyVector() : ptr(new T[cap]) { // ctor
     cout << "ctor" << endl;
     init(ptr);
   }

   MyVector(const MyVector& rhs) { // copy ctor
     cout << "copy ctor" << endl;
     sz = rhs.sz;
     ptr.reset(new T[cap]);
     init(ptr);
   }

   MyVector(MyVector&& rhs) { // move ctor
     cout << "move ctor" << endl;
     sz = rhs.sz;
     ptr = move(rhs.ptr);
   }

   T operator[](size_t i) const {
     return ptr[i];
   }

 private:
   const size_t cap = 100 * (1 << 20); // 100M elements capacity
   size_t sz = 0;
   unique_ptr<T[]> ptr;

   void init(const unique_ptr<T[]>& ptr) {
     for (size_t i = 0; i < cap; ++i) {
	ptr[i] = 456; // some initial value
     }
   }

 };

 template <typename F>
 static chrono::milliseconds time(F&& f) {
   using chrono::high_resolution_clock;
   using chrono::milliseconds;
   using chrono::duration_cast;

   auto t1 = high_resolution_clock::now();
   f();
   auto t2 = high_resolution_clock::now();
   return duration_cast<milliseconds>(t2 - t1);
 }

 static void ex1() {
   cout << "*** ex1 ***" << endl;

   auto f1 = []() { MyVector<int> v; };  
   cout << time(f1).count() << "ms\n" << endl;;

   auto f2 = []() {
		MyVector<int> v;
		MyVector<int> v1(v);
	      };
   cout << time(f2).count() << "ms\n" << endl;

   auto f3 = []() {
		MyVector<int> v;
		MyVector<int> v1(move(v));
	      };
   cout << time(f3).count() << "ms\n" << endl;  
 }

 MyVector<int> create() {
   return MyVector<int>();
 }

 void doSomething(MyVector<int> v) {
   return;
 }

 static void ex2() {
   cout << "*** ex2 ***" << endl;

   auto f1 = []() {
		auto v = create();
		doSomething(v);
	      };  
   cout << time(f1).count() << "ms\n" << endl;;

   auto f2 = []() {
		// cout << "create() gives a rvalue reference? "
		// 	   << is_compound<decltype(create())>::value
		// 	   << endl;
		doSomething(create());
	      };  
   cout << time(f2).count() << "ms\n" << endl;

   auto f3 = []() {
		// cout << "create() gives a rvalue reference? "
		// 	   << is_rvalue_reference<decltype(move(create()))>::value
		// 	   << endl;
		doSomething(move(create()));
	      };
   cout << time(f3).count() << "ms\n" << endl;

   return;
 }

 int main() {
   //ex1();
   ex2();
   return 0;
 }
***ex2***
ctor
copyctor
1732ms
ctor
881ms
ctor
movector
868ms

Intrestingly, for things like doSomething(create()), you may expect the move ctor to be invoked but it’s not. Recommended reading: https://en.cppreference.com/w/cpp/language/copy_elision. -fno-elide-constructors compiler flag can disable this compiler optimization in most compilers. It is also possible that some compilers don’t optimize this, so this is very compiler specific.

Templates

Also cover static vs dynamic typing and tradeoffs.

https://en.cppreference.com/w/cpp/language/template_parameters

Templates are recipes, not actual classes. That’s why they need to be present in the header files and not source files.

C++11 features

To cover

Some of these will be covered in C++11 features section above.