summaryrefslogtreecommitdiffhomepage
path: root/c++.html.markdown
diff options
context:
space:
mode:
Diffstat (limited to 'c++.html.markdown')
-rw-r--r--c++.html.markdown292
1 files changed, 246 insertions, 46 deletions
diff --git a/c++.html.markdown b/c++.html.markdown
index 26dfe111..a02e7e5b 100644
--- a/c++.html.markdown
+++ b/c++.html.markdown
@@ -5,6 +5,7 @@ contributors:
- ["Steven Basart", "http://github.com/xksteven"]
- ["Matt Kline", "https://github.com/mrkline"]
- ["Geoff Liu", "http://geoffliu.me"]
+ - ["Connor Waters", "http://github.com/connorwaters"]
lang: en
---
@@ -53,11 +54,11 @@ int main(int argc, char** argv)
// However, C++ varies in some of the following ways:
-// In C++, character literals are one byte.
-sizeof('c') == 1
+// In C++, character literals are chars
+sizeof('c') == sizeof(char) == 1
-// In C, character literals are the same size as ints.
-sizeof('c') == sizeof(10)
+// In C, character literals are ints
+sizeof('c') == sizeof(int)
// C++ has strict prototyping
@@ -148,7 +149,7 @@ namespace First {
namespace Second {
void foo()
{
- printf("This is Second::foo\n")
+ printf("This is Second::foo\n");
}
}
@@ -159,9 +160,9 @@ void foo()
int main()
{
- // Includes all symbols from `namesapce Second` into the current scope. Note
- // that simply `foo()` no longer works, since it is now ambiguous whether
- // we're calling the `foo` in `namespace Second` or the top level.
+ // Includes all symbols from namespace Second into the current scope. Note
+ // that simply foo() no longer works, since it is now ambiguous whether
+ // we're calling the foo in namespace Second or the top level.
using namespace Second;
Second::foo(); // prints "This is Second::foo"
@@ -244,7 +245,13 @@ cout << fooRef; // Prints "I am foo. Hi!"
// Doesn't reassign "fooRef". This is the same as "foo = bar", and
// foo == "I am bar"
// after this line.
+cout << &fooRef << endl; //Prints the address of foo
fooRef = bar;
+cout << &fooRef << endl; //Still prints the address of foo
+cout << fooRef; // Prints "I am bar"
+
+//The address of fooRef remains the same, i.e. it is still referring to foo.
+
const string& barRef = bar; // Create a const reference to bar.
// Like C, const values (and pointers and references) cannot be modified.
@@ -256,8 +263,8 @@ string tempObjectFun() { ... }
string retVal = tempObjectFun();
// What happens in the second line is actually:
-// - a string object is returned from `tempObjectFun`
-// - a new string is constructed with the returned object as arugment to the
+// - a string object is returned from tempObjectFun
+// - a new string is constructed with the returned object as argument to the
// constructor
// - the returned object is destroyed
// The returned object is called a temporary object. Temporary objects are
@@ -268,15 +275,15 @@ string retVal = tempObjectFun();
// code:
foo(bar(tempObjectFun()))
-// assuming `foo` and `bar` exist, the object returned from `tempObjectFun` is
-// passed to `bar`, and it is destroyed before `foo` is called.
+// assuming foo and bar exist, the object returned from tempObjectFun is
+// passed to bar, and it is destroyed before foo is called.
// Now back to references. The exception to the "at the end of the enclosing
// expression" rule is if a temporary object is bound to a const reference, in
// which case its life gets extended to the current scope:
void constReferenceTempObjectFun() {
- // `constRef` gets the temporary object, and it is valid until the end of this
+ // constRef gets the temporary object, and it is valid until the end of this
// function.
const string& constRef = tempObjectFun();
...
@@ -301,7 +308,71 @@ basic_string(basic_string&& other);
// Idea being if we are constructing a new string from a temporary object (which
// is going to be destroyed soon anyway), we can have a more efficient
// constructor that "salvages" parts of that temporary string. You will see this
-// concept referred to as the move semantic.
+// concept referred to as "move semantics".
+
+/////////////////////
+// Enums
+/////////////////////
+
+// Enums are a way to assign a value to a constant most commonly used for
+// easier visualization and reading of code
+enum ECarTypes
+{
+ Sedan,
+ Hatchback,
+ SUV,
+ Wagon
+};
+
+ECarTypes GetPreferredCarType()
+{
+ return ECarTypes::Hatchback;
+}
+
+// As of C++11 there is an easy way to assign a type to the enum which can be
+// useful in serialization of data and converting enums back-and-forth between
+// the desired type and their respective constants
+enum ECarTypes : uint8_t
+{
+ Sedan, // 0
+ Hatchback, // 1
+ SUV = 254, // 254
+ Hybrid // 255
+};
+
+void WriteByteToFile(uint8_t InputValue)
+{
+ // Serialize the InputValue to a file
+}
+
+void WritePreferredCarTypeToFile(ECarTypes InputCarType)
+{
+ // The enum is implicitly converted to a uint8_t due to its declared enum type
+ WriteByteToFile(InputCarType);
+}
+
+// On the other hand you may not want enums to be accidentally cast to an integer
+// type or to other enums so it is instead possible to create an enum class which
+// won't be implicitly converted
+enum class ECarTypes : uint8_t
+{
+ Sedan, // 0
+ Hatchback, // 1
+ SUV = 254, // 254
+ Hybrid // 255
+};
+
+void WriteByteToFile(uint8_t InputValue)
+{
+ // Serialize the InputValue to a file
+}
+
+void WritePreferredCarTypeToFile(ECarTypes InputCarType)
+{
+ // Won't compile even though ECarTypes is a uint8_t due to the enum
+ // being declared as an "enum class"!
+ WriteByteToFile(InputCarType);
+}
//////////////////////////////////////////
// Classes and object-oriented programming
@@ -349,7 +420,10 @@ public:
// These are called when an object is deleted or falls out of scope.
// This enables powerful paradigms such as RAII
// (see below)
- // Destructors must be virtual to allow classes to be derived from this one.
+ // The destructor should be virtual if a class is to be derived from;
+ // if it is not virtual, then the derived class' destructor will
+ // not be called if the object is destroyed through a base-class reference
+ // or pointer.
virtual ~Dog();
}; // A semicolon must follow the class definition.
@@ -394,6 +468,8 @@ int main() {
// Inheritance:
// This class inherits everything public and protected from the Dog class
+// as well as private but may not directly access private members/methods
+// without a public or protected method for doing so
class OwnedDog : public Dog {
void setOwner(const std::string& dogsOwner);
@@ -492,9 +568,10 @@ int main () {
/////////////////////
// Templates in C++ are mostly used for generic programming, though they are
-// much more powerful than generics constructs in other languages. It also
-// supports explicit and partial specialization, functional-style type classes,
-// and also it's Turing-complete.
+// much more powerful than generic constructs in other languages. They also
+// support explicit and partial specialization and functional-style type
+// classes; in fact, they are a Turing-complete functional language embedded
+// in C++!
// We start with the kind of generic programming you might be familiar with. To
// define a class or function that takes a type parameter:
@@ -506,7 +583,7 @@ public:
};
// During compilation, the compiler actually generates copies of each template
-// with parameters substituted, and so the full definition of the class must be
+// with parameters substituted, so the full definition of the class must be
// present at each invocation. This is why you will see template classes defined
// entirely in header files.
@@ -520,13 +597,13 @@ intBox.insert(123);
Box<Box<int> > boxOfBox;
boxOfBox.insert(intBox);
-// Up until C++11, you must place a space between the two '>'s, otherwise '>>'
-// will be parsed as the right shift operator.
+// Until C++11, you had to place a space between the two '>'s, otherwise '>>'
+// would be parsed as the right shift operator.
// You will sometimes see
// template<typename T>
-// instead. The 'class' keyword and 'typename' keyword are _mostly_
-// interchangeable in this case. For full explanation, see
+// instead. The 'class' keyword and 'typename' keywords are _mostly_
+// interchangeable in this case. For the full explanation, see
// http://en.wikipedia.org/wiki/Typename
// (yes, that keyword has its own Wikipedia page).
@@ -582,12 +659,15 @@ try {
// Do not allocate exceptions on the heap using _new_.
throw std::runtime_error("A problem occurred");
}
+
// Catch exceptions by const reference if they are objects
catch (const std::exception& ex)
{
- std::cout << ex.what();
+ std::cout << ex.what();
+}
+
// Catches any exception not caught by previous _catch_ blocks
-} catch (...)
+catch (...)
{
std::cout << "Unknown exception caught";
throw; // Re-throws the exception
@@ -597,8 +677,8 @@ catch (const std::exception& ex)
// RAII
///////
-// RAII stands for Resource Allocation Is Initialization.
-// It is often considered the most powerful paradigm in C++,
+// RAII stands for "Resource Acquisition Is Initialization".
+// It is often considered the most powerful paradigm in C++
// and is the simple concept that a constructor for an object
// acquires that object's resources and the destructor releases them.
@@ -619,9 +699,9 @@ void doSomethingWithAFile(const char* filename)
// Unfortunately, things are quickly complicated by error handling.
// Suppose fopen can fail, and that doSomethingWithTheFile and
// doSomethingElseWithIt return error codes if they fail.
-// (Exceptions are the preferred way of handling failure,
-// but some programmers, especially those with a C background,
-// disagree on the utility of exceptions).
+// (Exceptions are the preferred way of handling failure,
+// but some programmers, especially those with a C background,
+// disagree on the utility of exceptions).
// We now have to check each call for failure and close the file handle
// if a problem occurred.
bool doSomethingWithAFile(const char* filename)
@@ -721,6 +801,94 @@ void doSomethingWithAFile(const std::string& filename)
// all automatically destroy their contents when they fall out of scope.
// - Mutexes using lock_guard and unique_lock
+// containers with object keys of non-primitive values (custom classes) require
+// compare function in the object itself or as a function pointer. Primitives
+// have default comparators, but you can override it.
+class Foo {
+public:
+ int j;
+ Foo(int a) : j(a) {}
+};
+struct compareFunction {
+ bool operator()(const Foo& a, const Foo& b) const {
+ return a.j < b.j;
+ }
+};
+//this isn't allowed (although it can vary depending on compiler)
+//std::map<Foo, int> fooMap;
+std::map<Foo, int, compareFunction> fooMap;
+fooMap[Foo(1)] = 1;
+fooMap.find(Foo(1)); //true
+
+///////////////////////////////////////
+// Lambda Expressions (C++11 and above)
+///////////////////////////////////////
+
+// lambdas are a convenient way of defining an anonymous function
+// object right at the location where it is invoked or passed as
+// an argument to a function.
+
+// For example, consider sorting a vector of pairs using the second
+// value of the pair
+
+vector<pair<int, int> > tester;
+tester.push_back(make_pair(3, 6));
+tester.push_back(make_pair(1, 9));
+tester.push_back(make_pair(5, 0));
+
+// Pass a lambda expression as third argument to the sort function
+// sort is from the <algorithm> header
+
+sort(tester.begin(), tester.end(), [](const pair<int, int>& lhs, const pair<int, int>& rhs) {
+ return lhs.second < rhs.second;
+ });
+
+// Notice the syntax of the lambda expression,
+// [] in the lambda is used to "capture" variables
+// The "Capture List" defines what from the outside of the lambda should be available inside the function body and how.
+// It can be either:
+// 1. a value : [x]
+// 2. a reference : [&x]
+// 3. any variable currently in scope by reference [&]
+// 4. same as 3, but by value [=]
+// Example:
+
+vector<int> dog_ids;
+// number_of_dogs = 3;
+for(int i = 0; i < 3; i++) {
+ dog_ids.push_back(i);
+}
+
+int weight[3] = {30, 50, 10};
+
+// Say you want to sort dog_ids according to the dogs' weights
+// So dog_ids should in the end become: [2, 0, 1]
+
+// Here's where lambda expressions come in handy
+
+sort(dog_ids.begin(), dog_ids.end(), [&weight](const int &lhs, const int &rhs) {
+ return weight[lhs] < weight[rhs];
+ });
+// Note we captured "weight" by reference in the above example.
+// More on Lambdas in C++ : http://stackoverflow.com/questions/7627098/what-is-a-lambda-expression-in-c11
+
+///////////////////////////////
+// Range For (C++11 and above)
+///////////////////////////////
+
+// You can use a range for loop to iterate over a container
+int arr[] = {1, 10, 3};
+
+for(int elem: arr){
+ cout << elem << endl;
+}
+
+// You can use "auto" and not worry about the type of the elements of the container
+// For example:
+
+for(auto elem: arr) {
+ // Do something with each element of arr
+}
/////////////////////
// Fun stuff
@@ -735,21 +903,23 @@ class Foo {
virtual void bar();
};
class FooSub : public Foo {
- virtual void bar(); // overrides Foo::bar!
+ virtual void bar(); // Overrides Foo::bar!
};
// 0 == false == NULL (most of the time)!
bool* pt = new bool;
-*pt = 0; // Sets the value points by 'pt' to false.
+*pt = 0; // Sets the value points by 'pt' to false.
pt = 0; // Sets 'pt' to the null pointer. Both lines compile without warnings.
// nullptr is supposed to fix some of that issue:
int* pt2 = new int;
-*pt2 = nullptr; // Doesn't compile
+*pt2 = nullptr; // Doesn't compile
pt2 = nullptr; // Sets pt2 to null.
-// But somehow 'bool' type is an exception (this is to make `if (ptr)` compile).
+// There is an exception made for bools.
+// This is to allow you to test for null pointers with if(!ptr),
+// but as a consequence you can assign nullptr to a bool directly!
*pt = nullptr; // This still compiles, even though '*pt' is a bool!
@@ -770,20 +940,50 @@ Foo f1;
f1 = f2;
-// How to truly clear a container:
-class Foo { ... };
-vector<Foo> v;
-for (int i = 0; i < 10; ++i)
- v.push_back(Foo());
+///////////////////////////////////////
+// Tuples (C++11 and above)
+///////////////////////////////////////
+
+#include<tuple>
+
+// Conceptually, Tuples are similar to old data structures (C-like structs) but instead of having named data members ,
+// its elements are accessed by their order in the tuple.
+
+// We start with constructing a tuple.
+// Packing values into tuple
+auto first = make_tuple(10, 'A');
+const int maxN = 1e9;
+const int maxL = 15;
+auto second = make_tuple(maxN, maxL);
+
+// printing elements of 'first' tuple
+cout << get<0>(first) << " " << get<1>(first) << "\n"; //prints : 10 A
+
+// printing elements of 'second' tuple
+cout << get<0>(second) << " " << get<1>(second) << "\n"; // prints: 1000000000 15
+
+// Unpacking tuple into variables
+
+int first_int;
+char first_char;
+tie(first_int, first_char) = first;
+cout << first_int << " " << first_char << "\n"; // prints : 10 A
+
+// We can also create tuple like this.
+
+tuple<int, char, double> third(11, 'A', 3.14141);
+// tuple_size returns number of elements in a tuple (as a constexpr)
+
+cout << tuple_size<decltype(third)>::value << "\n"; // prints: 3
+
+// tuple_cat concatenates the elements of all the tuples in the same order.
-// Following line sets size of v to 0, but destructors don't get called,
-// and resources aren't released!
-v.empty();
-v.push_back(Foo()); // New value is copied into the first Foo we inserted in the loop.
+auto concatenated_tuple = tuple_cat(first, second, third);
+// concatenated_tuple becomes = (10, 'A', 1e9, 15, 11, 'A' ,3.14141)
-// Truly destroys all values in v. See section about temporary object for
-// explanation of why this works.
-v.swap(vector<Foo>());
+cout << get<0>(concatenated_tuple) << "\n"; // prints: 10
+cout << get<3>(concatenated_tuple) << "\n"; // prints: 15
+cout << get<5>(concatenated_tuple) << "\n"; // prints: 'A'
```
Further Reading: