summaryrefslogtreecommitdiffhomepage
path: root/c++.html.markdown
diff options
context:
space:
mode:
Diffstat (limited to 'c++.html.markdown')
-rw-r--r--c++.html.markdown227
1 files changed, 213 insertions, 14 deletions
diff --git a/c++.html.markdown b/c++.html.markdown
index 1a84efa4..26dfe111 100644
--- a/c++.html.markdown
+++ b/c++.html.markdown
@@ -4,6 +4,7 @@ filename: learncpp.cpp
contributors:
- ["Steven Basart", "http://github.com/xksteven"]
- ["Matt Kline", "https://github.com/mrkline"]
+ - ["Geoff Liu", "http://geoffliu.me"]
lang: en
---
@@ -32,8 +33,7 @@ one of the most widely-used programming languages.
// variable declarations, primitive types, and functions.
// Just like in C, your program's entry point is a function called
-// main with an integer return type,
-// though void main() is also accepted by most compilers (gcc, clang, etc.)
+// main with an integer return type.
// This value serves as the program's exit status.
// See http://en.wikipedia.org/wiki/Exit_status for more information.
int main(int argc, char** argv)
@@ -159,11 +159,12 @@ void foo()
int main()
{
- // Assume everything is from the namespace "Second"
- // unless otherwise specified.
+ // 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.
using namespace Second;
- foo(); // prints "This is Second::foo"
+ Second::foo(); // prints "This is Second::foo"
First::Nested::foo(); // prints "This is First::Nested::foo"
::foo(); // prints "This is global foo"
}
@@ -249,6 +250,59 @@ const string& barRef = bar; // Create a const reference to bar.
// Like C, const values (and pointers and references) cannot be modified.
barRef += ". Hi!"; // Error, const references cannot be modified.
+// Sidetrack: Before we talk more about references, we must introduce a concept
+// called a temporary object. Suppose we have the following code:
+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
+// constructor
+// - the returned object is destroyed
+// The returned object is called a temporary object. Temporary objects are
+// created whenever a function returns an object, and they are destroyed at the
+// end of the evaluation of the enclosing expression (Well, this is what the
+// standard says, but compilers are allowed to change this behavior. Look up
+// "return value optimization" if you're into this kind of details). So in this
+// 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.
+
+// 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
+ // function.
+ const string& constRef = tempObjectFun();
+ ...
+}
+
+// Another kind of reference introduced in C++11 is specifically for temporary
+// objects. You cannot have a variable of its type, but it takes precedence in
+// overload resolution:
+
+void someFun(string& s) { ... } // Regular reference
+void someFun(string&& s) { ... } // Reference to temporary object
+
+string foo;
+someFun(foo); // Calls the version with regular reference
+someFun(tempObjectFun()); // Calls the version with temporary reference
+
+// For example, you will see these two versions of constructors for
+// std::basic_string:
+basic_string(const basic_string& other);
+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.
+
//////////////////////////////////////////
// Classes and object-oriented programming
//////////////////////////////////////////
@@ -289,7 +343,7 @@ public:
// Functions can also be defined inside the class body.
// Functions defined as such are automatically inlined.
- void bark() const { std::cout << name << " barks!\n" }
+ void bark() const { std::cout << name << " barks!\n"; }
// Along with constructors, C++ provides destructors.
// These are called when an object is deleted or falls out of scope.
@@ -301,7 +355,7 @@ public:
}; // A semicolon must follow the class definition.
// Class member functions are usually implemented in .cpp files.
-void Dog::Dog()
+Dog::Dog()
{
std::cout << "A dog has been constructed\n";
}
@@ -324,7 +378,7 @@ void Dog::print() const
std::cout << "Dog is " << name << " and weighs " << weight << "kg\n";
}
-void Dog::~Dog()
+Dog::~Dog()
{
cout << "Goodbye " << name << "\n";
}
@@ -333,7 +387,7 @@ int main() {
Dog myDog; // prints "A dog has been constructed"
myDog.setName("Barkley");
myDog.setWeight(10);
- myDog.printDog(); // prints "Dog is Barkley and weighs 10 kg"
+ myDog.print(); // prints "Dog is Barkley and weighs 10 kg"
return 0;
} // prints "Goodbye Barkley"
@@ -342,7 +396,7 @@ int main() {
// This class inherits everything public and protected from the Dog class
class OwnedDog : public Dog {
- void setOwner(const std::string& dogsOwner)
+ void setOwner(const std::string& dogsOwner);
// Override the behavior of the print function for all OwnedDogs. See
// http://en.wikipedia.org/wiki/Polymorphism_(computer_science)#Subtyping
@@ -426,7 +480,7 @@ int main () {
Point up (0,1);
Point right (1,0);
// This calls the Point + operator
- // Point up calls the + (function) with right as its paramater
+ // Point up calls the + (function) with right as its parameter
Point result = up + right;
// Prints "Result is upright (1,1)"
cout << "Result is upright (" << result.x << ',' << result.y << ")\n";
@@ -434,6 +488,85 @@ int main () {
}
/////////////////////
+// Templates
+/////////////////////
+
+// 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.
+
+// We start with the kind of generic programming you might be familiar with. To
+// define a class or function that takes a type parameter:
+template<class T>
+class Box {
+public:
+ // In this class, T can be used as any other type.
+ void insert(const T&) { ... }
+};
+
+// During compilation, the compiler actually generates copies of each template
+// with parameters substituted, and 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.
+
+// To instantiate a template class on the stack:
+Box<int> intBox;
+
+// and you can use it as you would expect:
+intBox.insert(123);
+
+// You can, of course, nest templates:
+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.
+
+// You will sometimes see
+// template<typename T>
+// instead. The 'class' keyword and 'typename' keyword are _mostly_
+// interchangeable in this case. For full explanation, see
+// http://en.wikipedia.org/wiki/Typename
+// (yes, that keyword has its own Wikipedia page).
+
+// Similarly, a template function:
+template<class T>
+void barkThreeTimes(const T& input)
+{
+ input.bark();
+ input.bark();
+ input.bark();
+}
+
+// Notice that nothing is specified about the type parameters here. The compiler
+// will generate and then type-check every invocation of the template, so the
+// above function works with any type 'T' that has a const 'bark' method!
+
+Dog fluffy;
+fluffy.setName("Fluffy")
+barkThreeTimes(fluffy); // Prints "Fluffy barks" three times.
+
+// Template parameters don't have to be classes:
+template<int Y>
+void printMessage() {
+ cout << "Learn C++ in " << Y << " minutes!" << endl;
+}
+
+// And you can explicitly specialize templates for more efficient code. Of
+// course, most real-world uses of specialization are not as trivial as this.
+// Note that you still need to declare the function (or class) as a template
+// even if you explicitly specified all parameters.
+template<>
+void printMessage<10>() {
+ cout << "Learn C++ faster in only 10 minutes!" << endl;
+}
+
+printMessage<20>(); // Prints "Learn C++ in 20 minutes!"
+printMessage<10>(); // Prints "Learn C++ faster in only 10 minutes!"
+
+
+/////////////////////
// Exception Handling
/////////////////////
@@ -441,12 +574,13 @@ int main () {
// (see http://en.cppreference.com/w/cpp/error/exception)
// but any type can be thrown an as exception
#include <exception>
+#include <stdexcept>
// All exceptions thrown inside the _try_ block can be caught by subsequent
// _catch_ handlers.
try {
// Do not allocate exceptions on the heap using _new_.
- throw std::exception("A problem occurred");
+ throw std::runtime_error("A problem occurred");
}
// Catch exceptions by const reference if they are objects
catch (const std::exception& ex)
@@ -537,7 +671,7 @@ void doSomethingWithAFile(const char* filename)
{
FILE* fh = fopen(filename, "r"); // Open the file in read mode
if (fh == nullptr)
- throw std::exception("Could not open the file.");
+ throw std::runtime_error("Could not open the file.");
try {
doSomethingWithTheFile(fh);
@@ -586,8 +720,73 @@ void doSomethingWithAFile(const std::string& filename)
// vector (i.e. self-resizing array), hash maps, and so on
// all automatically destroy their contents when they fall out of scope.
// - Mutexes using lock_guard and unique_lock
+
+
+/////////////////////
+// Fun stuff
+/////////////////////
+
+// Aspects of C++ that may be surprising to newcomers (and even some veterans).
+// This section is, unfortunately, wildly incomplete; C++ is one of the easiest
+// languages with which to shoot yourself in the foot.
+
+// You can override private methods!
+class Foo {
+ virtual void bar();
+};
+class FooSub : public Foo {
+ 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 '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; // Sets pt2 to null.
+
+// But somehow 'bool' type is an exception (this is to make `if (ptr)` compile).
+*pt = nullptr; // This still compiles, even though '*pt' is a bool!
+
+
+// '=' != '=' != '='!
+// Calls Foo::Foo(const Foo&) or some variant (see move semantics) copy
+// constructor.
+Foo f2;
+Foo f1 = f2;
+
+// Calls Foo::Foo(const Foo&) or variant, but only copies the 'Foo' part of
+// 'fooSub'. Any extra members of 'fooSub' are discarded. This sometimes
+// horrifying behavior is called "object slicing."
+FooSub fooSub;
+Foo f1 = fooSub;
+
+// Calls Foo::operator=(Foo&) or variant.
+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());
+
+// 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.
+
+// Truly destroys all values in v. See section about temporary object for
+// explanation of why this works.
+v.swap(vector<Foo>());
+
```
-Futher Reading:
+Further Reading:
An up-to-date language reference can be found at
<http://cppreference.com/w/cpp>