C++
Table of Contents
- 1. Emacs Rtags
- 2. Tmp
- 3. Cast
- 4. Idioms
- 5. extern
- 6. Class
- 7. Concept
- 8. Library
- 9. Other
- 10. C++ Standards
- 11. Trouble Shooting
- 12. Operator Precedence
According to Linus, don't use C++
Quite frankly, even if the choice of C were to do nothing but keep the C++ programmers out, that in itself would be a huge reason to use C.
1 Emacs Rtags
Install rtags by rtags-install
with prompt location. Setup the PATH
to find rc and rdm. rtags-start-process-unless-running
to start
rdm. In the build dir of your project, create index by rc -J
.
. Then in C file buffer, you can use rtags-find-symbol-at-point
and rtags-find-references-at-point
to find symbol,
rtags-location-stack-forward
and rtags-location-stack-back
to jump
back and forth.
2 Tmp
static member function
: can useClassName::function()
directlystatic member variable
: only one object for all instance of the classstatic variable
: A static variable inside a function keeps its value between invocations. A static global variable or a function is "seen" only in the file it's declared instatic functions
: Static functions are not visible outside of the C file they are defined in.
3 Cast
3.1 In a word
static_cast
: ordinary type conversions.dynamic_cast
: converting pointers/references within an inheritance hierarchy.reinterpret_cast
: low-level reinterpreting of bit patterns. Use with extreme caution.const_cast
: casting away const/volatile. Avoid this unless you are stuck using a const-incorrect API.
3.2 C style cast: DO NOT USE
3.3 Static Cast
static_cast
is the first cast you should attempt to use.
It does things like implicit conversions between types
(such as int
to float
, or pointer to void*
),
and it can also call explicit conversion functions (or implicit ones).
In many cases, explicitly stating static_cast
isn't necessary,
but it's important to note that the T(something)
syntax is equivalent to (T)something
and should be avoided (more on that later).
A T(something, something_else)
is safe, however, and guaranteed to call the constructor.
static_cast
can also cast through inheritance hierarchies.
It is unnecessary when casting upwards (towards a base class),
but when casting downwards it can be used as long as it doesn't cast through virtual inheritance.
It does not do checking, however,
and it is undefined behavior to static_cast
down a hierarchy to a type that isn't actually the type of the object.
3.4 Const Cast
const_cast
can be used to remove or add const to a variable;
no other C++ cast is capable of removing it (not even reinterpret_cast
).
It is important to note that modifying a formerly const value is only undefined if the original variable is const;
if you use it to take the const off a reference to something that wasn't declared with const,
it is safe.
This can be useful when overloading member functions based on const, for instance.
It can also be used to add const to an object,
such as to call a member function overload.
const_cast
also works similarly on volatile,
though that's less common.
3.5 Dynamic Cast
dynamic_cast
is almost exclusively used for handling polymorphism.
You can cast a pointer or reference to any polymorphic type to any other class type
(a polymorphic type has at least one virtual function, declared or inherited).
You can use it for more than just casting downwards – you can cast sideways or even up another chain.
The dynamic_cast
will seek out the desired object and return it if possible.
If it can't, it will return NULL in the case of a pointer,
or throw std::bad_cast
in the case of a reference.
dynamic_cast
has some limitations, though.
It doesn't work if there are multiple objects of the same type in the inheritance hierarchy
(the so-called 'dreaded diamond') and you aren't using virtual inheritance.
It also can only go through public inheritance -
it will always fail to travel through protected or private inheritance.
This is rarely an issue, however, as such forms of inheritance are rare.
3.6 Reinterpret Cast
reinterpret_cast
is the most dangerous cast, and should be used very sparingly.
It turns one type directly into another -
such as casting the value from one pointer to another,
or storing a pointer in an int, or all sorts of other nasty things.
Largely, the only guarantee you get with reinterpret_cast
is that normally if you cast the result back to the original type,
you will get the exact same value (but not if the intermediate type is smaller than the original type).
There are a number of conversions that reinterpret_cast
cannot do, too.
It's used primarily for particularly weird conversions and bit manipulations,
like turning a raw data stream into actual data,
or storing data in the low bits of an aligned pointer.
3.7 C style cast
C casts are casts using (type)object or type(object). A C-style cast is defined as the first of the following which succeeds:
const_cast
static_cast
(though ignoring access restrictions)static_cast
(see above), thenconst_cast
reinterpret_cast
reinterpret_cast
, thenconst_cast
It can therefore be used as a replacement for other casts in some instances,
but can be extremely dangerous because of the ability to devolve into a reinterpret_cast
,
and the latter should be preferred when explicit casting is needed,
unless you are sure static_cast
will succeed or reinterpret_cast
will fail.
Even then, consider the longer, more explicit option.
C-style casts also ignore access control when performing a static_cast
,
which means that they have the ability to perform an operation that no other cast can.
This is mostly a kludge, though,
and in my mind is just another reason to avoid C-style casts.
4 Idioms
4.1 Curiously recurring template pattern (CRTP)
a class X derives from a class template instantiation using X itself as template argument.
General form:
tempalte <class T> class Base { // methods of Base can access members of Derived }; class Derived : public Base<Derived> {};
4.1.1 Static Polymorphism
It can achieve effect of virtual function but without the cost of dynamic polymorphism.
template <class T> struct Base { void implementation() { static_cast<T*>(this)->implementation(); } static void static_func() { T::static_sub_func(); } }; struct Derived : public Base<Derived> { void implementation(); static void static_sub_func(); };
4.1.2 Object Counter
counter<X>
and counter<Y>
are different class, so the counters are
separate for X and Y.
template <typename T> struct counter { static int objects_created; static int objects_alive; counter() { ++objects_created; ++objects_alive; } counter(const counter&) { ++objects_created; ++objects_alive; } protected: ~counter() { -- objects_alive; } }; template <typename T> int counter<T>::objects_created(0); template <typename T> int counter<T>::objects_alive(0); class X : counter<X> {}; class Y : counter<Y> {};
4.1.3 Polymorphic Copy Construction
When using polymorphism, one sometimes needs to create copies of objects by the base class pointer. So a clone virtual function is created in the base class, and defined in every derived classes. To avoid duplication in the derived class:
class Shape { public: virtual ~Shape() {}; virtual Shape *clone() const = 0; }; // CRTP class template <typename Derived> class Shape_CRTP : public Shape { public: virtual Shape *clone() const { return new Derived(static_cast<Derived const&>(*this)); } }; // ensure correct usage #define Derive_Shape_CRTP(Type) class Type: public Shape_CRTP(Type) // usage Derive_Shape_CRTP(Square) {}; Derive_Shape_CRTP(Circle) {};
5 extern
#ifdef __cplusplus extern "C" { #endif char *strcpy(char *, const char*); // ... #ifdef __cplusplus } #endif
extern "C"
is used to use a C header file in a C++ project.
It is called linkage convention.
C++ supports overload, but C does not.
C++ typically add more to a function name.
If this is the case, C++ will not find the correct C library because it looks for a different name.
By using this, during the linkage, the compiler will look for the original name, i.e. using C linkage method.
6 Class
6.1 Constructor
6.1.1 auto-gen by compiler
If you don't write, the compiler will generate:
- a copy constructor
- a copy assignment operator
- a destructor
- a default constructor(if you defined no constructors at all)
class A { public: A() {...} // default A(const A& rhs) {...} // copy ~A() {...} // destructor A& operator=(const A& rhs) {...} // copy assignment operator };
But they are generated only if they are needed.
For the operator=
, compiler will generate it iff:
- resulting code is legal
- reasonable to make sense
e.g.
class A { private: std::string &name; const int num; };
compiler will reject to generate operator=
because when doing assignment,
should the reference be modified?
C++ doesn't allow make a reference refer to another object.
Should the referred string be modified? It will affect other objects!
It is not legal to modify a const member either.
If you want to support copy assignment in the class containing reference or const,
you must define it yourself.
6.1.2 explicitly disallow the auto-gen
link time solution: Declare the copy constructor and the copy assignment operator private. So that compiler will not generate, outside can not see them. However, member and friend functions can still call them.
compile time solution:
Inheritate from Uncopyable
class who declared but didn't define the copy constructor and assginment operator.
This works because the compiler will try to
generate copy constructor and copy assignment when anybody tries to copy it,
but will of course fail.
It will give error says no instance of copy instructor implemented,
in other word you can't pass compilation.
class Uncopyable { protected: Uncopyable() {} ~Uncopyable() {} private: Uncopyable(const Uncopyable&); Uncopyable& operator=(const Uncopyable&); }; class A : private Uncopyable {}
6.1.3 Copy Constructor & Copy Assignment Operator
class A { public: A(); A(const A& rhs); // copy constructor A& operator=(const A& rhs) { // copy assignment operator return *this; // should return *this } }; A a1; // default constructor A a2(a1); // copy constructor a1 = a2; // copy assignment operator A a3 = a2; // copy constructor
if new object is being defined, a constructor has to be called.
That's why a3
is not calling a copy assignment operator.
copy constructor matters because it defines how an object is passed by value. In particular, pass-by-value means "call the copy constructor".
6.1.4 Copy-and-swap Idiom
To create an exception safe implementation of overloaded assignment operator. The copy assignment opeartor implementation can cause two kinds of unsafety:
- self-assignment unsafe
- exception unsafe
Self-assignment should be properly handled.
It can appear often, e.g.
a[i] = a[j];
in the case i=j,
*px = *py;
.
The following code explain the two kinds of unsafe.
- self-assignment unsafe:
rhs.pa
is already deleted ifrhs == this
- exception unsafe: if exception happens during new, pa will contains a pointer to a deleted A.
class A {}; class B { private: A * pa; }; B& B::operator=(const B& rhs) { if (this == &rhs) return * this; // get rid of self-assignment unsafe delete pa; pa = new A(* rhs.pa); return * this; }
According to https://en.wikibooks.org/wiki/More_C++_Idioms/Copy-and-swap, the copy and swap is:
Create a temporary and swap idiom acquires new resource before it forfeits its current resource. To acquire the new resource, it uses RAII idiom. If the acquisition of the new resource is successful, it exchanges the resources using the non-throwing swap idiom. Finally, the old resource is released as a side effect of using RAII in the first step.
The code follows:
class B { // use std::swap? void swap(B& rhs) { std::swap(xx,rhs.xx); } }; // v1: explicitly create new. BAD B& B::operator=(const B& rhs) { B tmp(rhs); swap(tmp); return * this; } // v2: use pass-by-value as temporary value. GOOD. // better optimization B& B::operator=(B rhs) { swap(rhs); return * this; }
6.1.5 Move Constructor & Move Assignment Opeartor
Move constructor enables you to implement move semantics, which can significantly improve the performance of your applications. Move semantics enables you to write code that transfers resources (such as dynamically allocated memory) from one object to another. Move semantics works because it enables resources to be transferred from temporary objects that cannot be referenced elsewhere in the program.
To implement move semantics, you typically provide a move constructor, and optionally a move assignment operator (operator=), to your class. Copy and assignment operations whose sources are rvalues then automatically take advantage of move semantics. Reference: Move Constructors and Move Assignment Operators from Microsoft.
Move Constructor Example:
// Rvalue Reference MemoryBlock(MemoryBlock&& other) : _data(nullptr) , _length(0) { // copy _data = other._data; _length = other._length; // set source object fields to default, to avoid multiple free other._data = nullptr; other._length = 0; }
Move Assignment Operator Example:
MemoryBlock& operator=(MemoryBlock&& other) { // avoid self-assignment if (this != &other) { // Free the existing resource. delete[] _data; // Copy the data pointer and its length from the // source object. _data = other._data; _length = other._length; // Release the data pointer from the source object so that // the destructor does not free the memory multiple times. other._data = nullptr; other._length = 0; } return *this; }
6.1.6 explicit constructor
The explicit prevents the class from being used to perform implicit type conversions, though they may still be used for explicit type conversions. Always declare it explicit unless you have a good reason for allowing a constructor to be used for implicit type conversions.
class A { public: explicit A(int x=0, bool b=true); explicit A(char c); // non-default can also have explicit }; void func(A a); A a1; func(a1); // ok A a2(20); // ok func(20); // error, cannot convert int to A implicitly func(A(20)); // use B constructor to explicit convert
6.1.7 initialization
6.1.7.1 Default Constructor
One that can be called without any arguments is called default constructor. Compilers will automatically call default constructors for data members of user-defined types when those data members are not on initialization list.
6.1.7.2 initialization
Data members that are const or references must be initialized; they cant be assigned. Do NOT call constructors within each other. If init is too many, move them into a private function, and call the function in all constructors.
The initialization orders are defined by: Base classes are initialized before derived classes; within a class, data members are initialized in the order in which they declared, not the position in initialization list.
I did a test for the copy constructor:
#include <iostream> class A { public: A() {} ~A() {} int get() {return a;} void set(int aa) { a = aa; } private: int a = 8; }; int main() { A *a = new A(); a->set(9); A *b = new A(*a); std::cout << a->get() << "\n"; std::cout << b->get() << "\n"; }
Both the outputs are 9, so the initialization a=8
is not called when doing copy construction
6.2 virtual
6.2.1 Bottom Line
- polymorphic base classes should declare virtual destructors. If a class has virtual functions, it should have virtual destructor
- Classes should not have virtual destructor if it is not designed to be
- base class, or
- used polymorphically
6.2.2 Description
Factory Function
:
a function that returns a base class pointer
to a newly-created derived class object.
class TimeKeeper { public: TimeKeeper(); virtual ~TimeKeeper(); // must have the virtual, or disaster }; class AtomicClock : public TimeKeeper {}; class WaterClock : public TimeKeeper {}; class WristWatch : public TimeKeeper {}; TimeKeeper *getTimeKeeper(); // can return any one TimeKeeper *ptk = getTimeKeeper(); // ... delete ptk;
If no virtual, the delete ptk
will call the destructor of TimeKeeper,
so the AtomicClock part of the struct will be never destroyed.
But do not declare every destructor virtual: If a class does not contain virtual functions, it is not meant to be used as a base class. DO NOT use virtual destructor for it. Because:
- virtual requires the objects carry information that can be used at runtime to determine which virtual function to invoke. It will increase the size.
- it is not the same as the counterpart in C, not portable.
Never call virtual functions during construction or destruction. Because during base construction, virtual functions never go down into the derived class.
6.2.3 Virtual vs. Non-Virtual
Without virtual
you get early binding. Which implementation of the method is used gets decided at compile time based on the type of the pointer that you call through.
With virtual
you get late binding. Which implementation of the method is used gets decided at run time based on the type of the pointed-to object - what it was originally constructed as. This is not necessarily what you'd think based on the type of the pointer that points to that object.
class Base { public: void Method1 () { std::cout << "Base::Method1" << std::endl; } virtual void Method2 () { std::cout << "Base::Method2" << std::endl; } }; class Derived : public Base { public: void Method1 () { std::cout << "Derived::Method1" << std::endl; } void Method2 () { std::cout << "Derived::Method2" << std::endl; } }; Base* obj = new Derived (); // Note - constructed as Derived, but pointer stored as Base* obj->Method1 (); // Prints "Base::Method1" obj->Method2 (); // Prints "Derived::Method2"
6.2.4 Virtual vs. Pure Virtual
- virtual function
can
be overriden - the pure virtual
must
be implemented in non-abstract class
6.3 Inheritance
6.3.1 public inheritance
"is-a" relation.
Private inheritance means "is-implemented-in-terms-of". Private inheritance means nothing during software design, only during software implementation. Means Derived objects are implemented in terms of Base objects, nothing more.
Composition means either "has-a" or "is-implementated-in-terms-of".
6.3.2 hide method
class Base { private: int x; public: virtual void mf1() = 0; virtual void mf1(int); virtual void mf2(); void mf3(); void mf3(double); }; class Derived : public Base { public: // using Base::mf1; // making all things in Base named mf1 and mf3 // using Base::mf3; // visible and public in Derived's scope virtual void mf2() { // forwarding function Base::mf1(); } virtual void mf1(); void mf3(); void mf4(); };
the mf3
in Derived will hide both of the mf3
in Base.
The rationale behind this behavior is that
it prevents you from accidentally inheriting overloads from distant base classes
when you create a new derived class in a library or application framework.
6.3.3 make it visible
using
declarations- forwarding functions
6.4 Overload
Same name but different signature.
void print(int i) { cout << "Printing int: " << i << endl; } void print(double f) { cout << "Printing float: " << f << endl; } void print(char* c) { cout << "Printing character: " << c << endl; }
Operator Overload
inline bool operator==(Date a, Data b) { return a.day() == b.day() && a.month() == b.month() && a.year() == b.year(); } bool operator!=(Date, Date); bool operator<(Date, Date) bool operator>(Date, Date) Date& operator++(Date &d); Date& operator--(Date &d); Date& operator+=(Date &d, int n); Date& operator-=(Date &d, int n); Date operator+(Date d, int n); Date operator-(Date d, int n); ostream& operator<<(ostream&, Date d); istream& operator>>(istream&, Date &d);
6.5 Polymorphism
It is the ability to redefine methods for derived classes.
class Polygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } }; class Rectangle: public Polygon { public: int area() { return width*height; } }; class Triangle: public Polygon { public: int area() { return width*height/2; } };
6.5.1 Static Polymorphism
The Curiously Recurring Template Pattern (CRTP) is an idiom in C++ in which a class X derives from a class template instantiation using X itself as template argument1. It is also known as F-bound polymorphism\cite{canning1989f}. One of the use case of CRTP is static polymorphism. Generally speaking, I have a base class and some derived class, and I want to have a ~~static virtual'' function that is implemented differently in different subclasses. I think such ~~static virtual function'' does not exist. But we can simulate it.
template <class T> struct Base { void interface() { // ... static_cast<T*>(this)->implementation(); // ... } static void static_func() { // ... T::static_sub_func(); // ... } }; struct Derived : Base<Derived> { void implementation(); static void static_sub_func(); };
6.6 non-member function
void clearBrowser(WebBrowser& wb) { wb.clearCache(); wb.clearHistory(); wb.removeCookies(); } class WebBrowser { public: void clearCache(); void clearHistory(); void removeCookies(); void clearEverything(); };
Prefer use the non-member function, because then less function can have access to private data, thus better encapsulate.
6.6.1 A common pattern
Putting all convenience functions in multiple header files, but one namespace.
webbrowser.h
namespace WebBrowserStuff { class WebBrowser {...}; void clearBrowser(WebBrowser& wb); // ... }
webbrowserbookmarks.h
namespace WebBrowserStuff { // bookmark related functions }
6.7 friendship
6.7.1 Friend function
private and protected member cannot be accessed outside the class, except friends.
class A { public: friend A func(A a); // declare friend private: int m; }; A func(A& a) { A res; res.m = a.m; // access both param and return value return res; }
6.7.2 Friend Class
a class whose member functions can access private and protected member of another class.
class Rectangle { int width, height; public: int area () {} void convert (Square a) { width = a.side; // access side in Square height = a.side; } }; class Square { friend class Rectangle; // friend declaration private: int side; public: Square (int a) : side(a) {} };
6.8 Nested Class
class enclose { class nested1; // forward declaration class nested2; // forward declaration class nested1 {}; // definition of nested class }; class enclose::nested2 { }; // definition of nested class
- The nested class can access private and protected member of the enclosing class, but have separate
this
pointer. - The friend of the nested class cannot access private and protected member of the enclosing class.
7 Concept
7.1 reference vs. value
A good writeup: http://thbecker.net/articles/rvalue_references/section_01.html
The original definition for C:
An lvalue is an expression e that may appear on the left or on the right hand side of an assignment, whereas an rvalue is an expression that can only appear on the right hand side of an assignment.
The changed definition for C++:
An lvalue is an expression that refers to a memory location and allows us to take the address of that memory location via the & operator. An rvalue is an expression that is not an lvalue.
7.1.1 Pass-by Problems
Pass-by-value has two problems. Apart from copy problem, there's also a slicing problem, i.e. when a derived class object is passed by value as a base class object, the base class constructor is called, thus the part of the subclass outside the base class will be sliced away.
Also do NOT just pass by value because the struct seems to be small.
- it can be large, by inheritance
- the copy constructor may be costly: a object contain little more than a pointer, but the constructor will copy everything they point to.
- some compiler treat built-in type and structure differently. Some will refuse to put a struct that only contains a double into register, but it will surely put a double into register.
So some situation pass by value is more efficient though:
- built-in type(e.g. int)
- iterators and function objects in STL, they are designed to pass by value
On the other hand, reference is often implemented as pointer.
However, there're situations where you have no way but to return a value.
Return a stack local variable as a reference does not make sense becasue the variable will not exist outside the function.
When you return a heap variable, be careful. E.g. in a operator*
method, return a heap variable is a disaster.
w = x * y * z;
the result of x*y
will never be free-d.
7.1.2 rvalue and lvalue
______ ______ / X \ / / \ \ | l | x | pr | \ \ / / \______ X______/ gl r
7.1.2.1 lvalue
An lvalue is an expression that identifies a non-temporary object or a non-member function.
- The name of a variable or function in scope
- Function call or overloaded operator expression if the function's or overloaded operator's return type is an lvalue reference
- string literal
A glvalue (~~generalized'' lvalue) is an lvalue or an xvalue.
7.1.2.2 rvalue
An rvalue is an expression that is either a prvalue or an xvalue. A prvalue (~~pure'' rvalue) is an rvalue that is not an xvalue.
- prvalue
A prvalue ("pure" rvalue) is an expression that identifies a temporary object (or a subobject thereof) or is a value not associated with any object.
- literal(except string literal): 42, true
- the result of calling a function whose return type is not a reference is a prvalue.
- xvalue
An
xvalue
(an “eXpiring” value) also refers to an object, usually near the end of its lifetime (so that its resources may be moved, for example). An xvalue is the result of certain kinds of expressions involving rvalue references. E.g. the result of calling a function whose return type is an rvalue reference is an xvalue.
7.2 rvalue reference
C++11 introduce ravlue reference to enable move semantic.
std::vector<T> is essentially a C-style array and the size.
Say a std::vector<T> temporary is created or returned from a function.
To accept the return value, a new vector should be created, and all the internal C-array will be copied.
When using a move constructor,
it takes the rvalue reference of the temporary vector (vector<>&&
),
and copy the pointer to the internal C-style array out of the rvalue into the new vector,
than set the pointer inside the temporary vector to NULL.
Since the temporary vector is about to expire, and no one would use it any more,
it is safe.
And since the pointer is NULL, no space will be freed upon deconstructing the temporay vector.
Rvalue Reference is important because it supports the implementation of move constructor (enable move semantic) and perfect forwarding. We discuss perfect forwarding here.
7.2.1 The move semantic and swap
template <class T> typename remove_reference<T>::type&& move (T&& arg) noexcept; template <class T> void swap (T& a, T& b) { T c(std::move(a)); a=std::move(b); b=std::move(c); } template <class T, size_t N> void swap (T &a[N], T &b[N]) { for (size_t i = 0; i<N; ++i) swap (a[i],b[i]); }
Example
// move takes an object, invalidate it, and return the rvalue. std::string bar = "bar-string"; myvector.push_back (std::move(bar)); // Now bar is valid but has no valid content, while the vector contains the string.
7.2.2 Perfect Forwarding
Perfect forwarding reduces the need for overloaded functions and helps avoid the forwarding problem. The forwarding problem can occur when you write a generic function that takes references as its parameters and it passes (or forwards) these parameters to another function. For example, if the generic function takes a parameter of type const T&, then the called function cannot modify the value of that parameter. If the generic function takes a parameter of type T&, then the function cannot be called by using an rvalue (such as a temporary object or integer literal).
Ordinarily, to solve this problem, you must provide overloaded versions of the generic function that take both T& and const T& for each of its parameters.
As a result, the number of overloaded functions increases exponentially with the number of parameters.
(For instance the following code, to write a generic factory
function,
we need to try all combination of const T&
and T&
for every type pair of W,X,Y,Z
).
Rvalue references enable you to write one version of a function that accepts arbitrary arguments
and forwards them to another function as if the other function had been called directly.
For example, following code
struct W { W(int&, int&) {} }; struct X { X(const int&, int&) {} }; struct Y { Y(int&, const int&) {} }; struct Z { Z(const int&, const int&) {} }; // Version 1 template <typename T, typename A1, typename A2> T* factory(A1& a1, A2& a2) { return new T(a1, a2); } int a = 4, b = 5; W* pw = factory<W>(a, b); Z* pz = factory<Z>(2, 2); // error // Version 2: using R reference template <typename T, typename A1, typename A2> T* factory(A1&& a1, A2&& a2) { return new T(std::forward<A1>(a1), std::forward<A2>(a2)); } Z* pz = factory<Z>(2, 2); // correct
std::forward function forwards the parameters of the factory function to the constructor of the template class.
7.2.3 Other properties
- The compiler treats a named rvalue reference as an lvalue and an unnamed rvalue reference as an rvalue.
- You can cast an lvalue to an rvalue reference.
static_cast<MemoryBlock&&>(block)
For detail, refer to Rvalue Reference by Microsoft.
7.3 lambda
Constructs a closure: an unnamed function object capable of capturing variables in scope.
reference:
7.3.1 syntax
- Full declaration:
[ capture-list ] ( params ) mutable(optional) exception attribute -> ret { body }
- Declaration of a const lambda: the objects captured by copy cannot be modified.
[ capture-list ] ( params ) -> ret { body }
for example
[]()->int { return 2; }
- Omitted trailing-return-type
[ capture-list ] ( params ) { body }
if the body
contains nothing but a single return statement, the return type is that expression's type. Otherwise return type is void
.
- Omitted parameter list
take no parameters.
[ capture-list ] { body }
7.3.2 Explanations
mutable
: allows body to modify the parameters captured by copy, and to call their non-const member functionsexception
: provides the exception specification or the noexcept clause for operator() of the closure typeattribute
: provides the attribute specification for operator() of the closure typecapture-list
: a comma-separated list of zero or more captures[a,&b]
: where a is captured by value and b is captured by reference.[this]
: captures the this pointer by value[&]
: captures all automatic variables odr-used in the body of the lambda by reference[=]
: captures all automatic variables odr-used in the body of the lambda by value[]
: captures nothing
7.4 Smart Pointer
unique_ptr
- cannot be copied
shared_ptr
- can be copied. Will destroy when the last reference destroy.
weak_ptr
- reference to an object, but does not increase the
count for it. It must be converted to
shared_ptr
before use.
The weak_ptr
can help break the reference-count cycle problem.
class widget { shared_ptr<gadget> g; }; class gadget { weak_ptr<widget> w; };
If both are shared_ptr
, the ownership is not clear, so destructing
them would be a problem.
std::unique_ptr<Type> ptr; // ensure that the pointer is deleted after going out of scope. std::shared_ptr<Type> ptr;
7.5 Exception Handling
C++ Exception is handled by try-catch clause. Catch accept an
argument, a reference to the type of the exception. It can accept
...
, meaning all kinds of exceptions. The throw expression accepts
one argument. The type of that argument should match the type of the
argument of catch. If throw is used without argument, it means
Rethrows the currently handled exception. So, throw can accept an int
value, as long as the corresponding catch accept an int.
Catching of exception usually is the reference. The std::exception
is
the standard base class for exceptions. The signature is:
class exception { public: exception () throw(); exception (const exception&) throw(); exception& operator= (const exception&) throw(); virtual ~exception() throw(); virtual const char* what() const throw(); }
The what virtual function should be overwritten and returns a null-terminated string.
7.5.1 Exception specification
This is deprecated. In the declaration of a function, you can add a throw keyword and the type of exception in parenthesis. Throw is a specifier, and is part of the function type.
double myfunction (char param) throw (int);
If the function throws exception other than int
, the function
std::unexpected
is called. Function without throw specifier will never
call std::unexpected
, and do the normal exception handling. If here
there's no type in the parenthesis, it means the function should not
throw any exception.
noexcept
is the current in use one. If no argument provided, it is
same as noexcept(true)
. If argument is provided, it is evaluated and
if it evaluates to true, it means this function is not throwing any
exception. Otherwise the function is potentially throwing. throw ()
is
same as noexcept(true)
but is deprecated.
7.6 Template
7.6.1 Template specialization
Use when you want to define a different implementation for a template when a specific type is passed as template parameter.
The syntax is this: put an empty inside the brackets, and put a <char>
after the class name. When instantiate this class with char
, it will
use the specialized one.
template <class T> class mycontainer { ... }; template <> class mycontainer <char> { ... };
So only have the second form is not valid.
7.6.2 Non-type parameter
The parameter can not just be a type name, but also a regular type.
template <class T, int N> class mysequence { T memblock [N]; public: void setmember (int x, T value); T getmember (int x); };
It can have default values:
template <class T=char, int N=10> class mysequence {..};
Then the following calls are equivalent:
mysequence<> myseq; mysequence<char,10> myseq;
7.6.3 Template Implementation
See https://isocpp.org/wiki/faq/templates#templates-defn-vs-decl
The compiler must see two things at the same time in order to instantiate a template class. Because the instantiated template class are generated by the compiler.
- all the template implementation
- the type used to instantiate the template
For example:
foo.h
template<typename T> class Foo { public: void f(); void g(); void h(); }; template<typename T> inline void Foo<T>::f() {}
foo.cpp
#include <iostream> #include "foo.h" template<typename T> void Foo<T>::g() { std::cout << "Foo<T>::g()\n"; } template<typename T> void Foo<T>::h() { std::cout << "Foo<T>::h()\n"; }
main.cpp
#include "foo.h" int main() { Foo<int> x; x.f(); x.g(); x.h(); }
The link will generate error that cannot find g
and h
definition. Of course moving the definition in foo.cpp
to foo.h
can solve the problem, but it will make the header file too big.
Another way: put template class Foo<int>;
at the end of foo.cpp
,
thus compiler can see the Foo<int>
and implementation at the same
time.
You can also have a foo-impl.cpp
for adding this, but it should
include foo.cpp
foo-impl.cpp
#include "foo.cpp" template class Foo<int>;
8 Library
8.1 Stream
8.1.1 file stream
#include <fstream> ofstream myfile; myfile.open("a.txt"); if (myfile.is_open()) { myfile << "..."; myfile.close(); } // after close, it can used to open another file myfile.open("b.txt"); myfile.close();
8.1.1.1 When to flush
file.close()
- buffer is full
flush
,endl
used as manipulatorsfile.sync()
8.1.1.2 mode
open flag:
flag | desription |
:------------ | :---------------------------------------------------------------------------------- |
ios::in | input |
ios::out | output |
ios::binary | binary mode |
ios::ate | initial position to the end of file |
ios::app | all output operations are performed at the end of the file, append |
ios::trunc | if the file is opened for output and already exists, previous content is replaced |
Default:
class | default mode | New flag action |
:--------- | :------------------- | :---------------- |
ofstream | ios::out | add |
ifstream | ios::in | add |
fstream | ios::in 1 ios::out | overwrite |
binary mode cannot use >>, <<, getline, but use
write(memory_block, size); read(memory_block, size);
8.1.1.3 seek
- tellg(): get get position
- tellp(): get put position
- seekg(position): count from the beginning
- seekp(position)
- seekg(offset, direction);
- seekp(offset, direction);
direction | description |
:---------- | :------------ |
ios::beg | beginning |
ios::cur | current |
ios::end | end |
8.1.2 iostream
#include <iostream> int price; cin>>price;
If the input is not integer, the program will continue without setting price's value. Then if a is used afterwards, undefined behavior.
To add a validation process, we need to use stringstream:
#include <sstream> string mystr; getline(cin, mystr); stringstream ss = stringstream(mystr); // validate ss int price; ss >> price;
Always use getline instead of cin directly
while(getline(cin, line)) {;} while(getline(fs, line)) {;}
8.2 String
8.2.1 Constructor
// default string(); // copy string (const string& str); // substring string (const string& str, size_t pos, size_t len = npos); // from c-string string (const char* s); // from sequence string (const char* s, size_t n); // fill string (size_t n, char c); // range template <class InputIterator> string (InputIterator first, InputIterator last);
8.2.2 operator=
// string string& operator= (const string& str); // c-string string& operator= (const char* s); // character string& operator= (char c);
8.2.3 handy routine
8.2.3.1 trim a string
#include <algorithm> #include <functional> #include <cctype> #include <locale> // trim from start static inline std::string <rim(std::string &s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace)))); return s; } // trim from end static inline std::string &rtrim(std::string &s) { s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end()); return s; } // trim from both ends static inline std::string &trim(std::string &s) { return ltrim(rtrim(s)); }
8.2.3.2 split a string
string s("Somewhere down the road"); istringstream iss(s); do { string sub; iss >> sub; cout << "Substring: " << sub << endl; } while (iss);
std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) { std::stringstream ss(s); std::string item; while (std::getline(ss, item, delim)) { elems.push_back(item); } return elems; } std::vector<std::string> split(const std::string &s, char delim) { std::vector<std::string> elems; split(s, delim, elems); return elems; }
More flexible version:
/** * Delim by ANY characters in delim string */ std::vector<std::string> utils::split(std::string s, std::string delim) { std::size_t prev = 0, pos; std::vector<std::string> ret; while ((pos = s.find_first_of(delim, prev)) != std::string::npos) { if (pos > prev) ret.push_back(s.substr(prev, pos-prev)); prev = pos+1; } if (prev < s.length()) { ret.push_back(s.substr(prev, std::string::npos)); } return ret; }
8.2.3.3 better split string
// to std::cout copy( istream_iterator<string>(iss), istream_iterator<string>(), ostream_iterator<string>(cout, "\n") ); // to a vector vector<string> tokens; copy( istream_iterator<string>(iss), istream_iterator<string>(), back_inserter(tokens) ); // construct the vector directly vector<string> tokens{ istream_iterator<string>{iss}, istream_iterator<string>{} };
8.2.4 member function
8.2.4.1 iterator
- begin()
- end()
8.2.4.2 capacity
- size(): length of string
- length(): length of string
- empty()
- clear()
8.2.4.3 access
- operator[]
- at()
- back(): A reference to the last character in the string
8.2.4.4 mofifier
- operator+=
// string (1) string& operator+= (const string& str); // c-string string& operator+= (const char* s); // character string& operator+= (char c);
- append
// string string& append (const string& str); // substring string& append (const string& str, size_t subpos, size_t sublen); // c-string string& append (const char* s); // buffer string& append (const char* s, size_t n); // fill string& append (size_t n, char c); // range template <class InputIterator> string& append (InputIterator first, InputIterator last);
- pushback(char):
void push_back (char c);
- insert(): before the character indicated by pos (or p)
// string string& insert (size_t pos, const string& str); // substring string& insert (size_t pos, const string& str, size_t subpos, size_t sublen); // c-string string& insert (size_t pos, const char* s); // buffer string& insert (size_t pos, const char* s, size_t n); // fill string& insert (size_t pos, size_t n, char c); void insert (iterator p, size_t n, char c); // single character iterator insert (iterator p, char c); // range template <class InputIterator> void insert (iterator p, InputIterator first, InputIterator last);
- erase(): erase part of the string
// sequence string& erase (size_t pos = 0, size_t len = npos); // character: Erases the character pointed by p iterator erase (iterator p); // range: [first,last) iterator erase (iterator first, iterator last);
- replace()
// string string& replace (size_t pos, size_t len, const string& str); string& replace (iterator i1, iterator i2, const string& str); // substring string& replace ( size_t pos, size_t len, const string& str, size_t subpos, size_t sublen ); // c-string string& replace (size_t pos, size_t len, const char* s); string& replace (iterator i1, iterator i2, const char* s); // buffer string& replace (size_t pos, size_t len, const char* s, size_t n); string& replace (iterator i1, iterator i2, const char* s, size_t n); // fill string& replace (size_t pos, size_t len, size_t n, char c); string& replace (iterator i1, iterator i2, size_t n, char c); // range template <class InputIterator> string& replace (iterator i1, iterator i2, InputIterator first, InputIterator last );
8.2.4.5 operation
- cstr(): A program shall not alter any of the characters in this sequence.
- copy(): Copies a substring of the current value of the string object
into the array pointed by s. does not append null-terminator
size_t copy (char* s, size_t len, size_t pos = 0) const;
- find(): the first occurrence of the sequence specified after pos
// string size_t find (const string& str, size_t pos = 0) const; // c-string size_t find (const char* s, size_t pos = 0) const; // buffer size_t find (const char* s, size_t pos, size_t n) const; // character size_t find (char c, size_t pos = 0) const;
- substr(): Returns a newly constructed string object
with its value initialized to a copy of a substring of this object
string substr (size_t pos = 0, size_t len = npos) const;
- compare()
// string int compare (const string& str) const; // substrings int compare (size_t pos, size_t len, const string& str) const; int compare (size_t pos, size_t len, const string& str, size_t subpos, size_t sublen ) const; // c-string int compare (const char* s) const; int compare (size_t pos, size_t len, const char* s) const; // buffer int compare (size_t pos, size_t len, const char* s, size_t n) const;
return: 0, -, +
- npos: max value of sizet
static const size_t npos = -1;
8.2.5 non-member function
8.2.5.1 operator +
// string string operator+ (const string& lhs, const string& rhs); // c-string string operator+ (const string& lhs, const char* rhs); string operator+ (const char* lhs, const string& rhs); // character string operator+ (const string& lhs, char rhs); string operator+ (char lhs, const string& rhs);
8.2.5.2 rational
bool operator== (const string& lhs, const string& rhs); bool operator== (const char* lhs, const string& rhs); bool operator== (const string& lhs, const char* rhs); bool operator!= (const string& lhs, const string& rhs); bool operator!= (const char* lhs, const string& rhs); bool operator!= (const string& lhs, const char* rhs); bool operator< (const string& lhs, const string& rhs); bool operator< (const char* lhs, const string& rhs); bool operator< (const string& lhs, const char* rhs); bool operator<= (const string& lhs, const string& rhs); bool operator<= (const char* lhs, const string& rhs); bool operator<= (const string& lhs, const char* rhs); bool operator> (const string& lhs, const string& rhs); bool operator> (const char* lhs, const string& rhs); bool operator> (const string& lhs, const char* rhs); bool operator>= (const string& lhs, const string& rhs); bool operator>= (const char* lhs, const string& rhs); bool operator>= (const string& lhs, const char* rhs);
8.2.5.3 >>
extract string from stream
istream& operator>> (istream& is, string& str); ostream& operator<< (ostream& os, const string& str);
8.2.5.4 getline
get line from stream into string
istream& getline (istream& is, string& str, char delim); // delim istream& getline (istream& is, string& str); // new line
9 Other
9.1 Tips
main~函数的返回类型必须是 ~int
- 发出警告:
-Wall
cin>>a
遇到EOF
为假。遇到<C-D>
为假。./a.out <infile >outfile
9.1.1 function object
Objects that act like functions.
Such objects come from classes that overload operator()
.
9.1.2 auto
auto g = bind(f, a, b, _2, c, _1);
此后,调用 g(-1,-2)
等价于调用f,并把 _1
换成 -1
, _2
换成 -2
.
9.1.3 at
适用于 string
, vector
, deque
, array
c.at(n)
返回下表为~n~的元素的引用。如果下标越界,可以抛出 out_of_range
异常。
9.1.4 decltype
struct A { double x; }; const A* a = new A{0}; decltype( a->x ) x3; // type of x3 is double (declared type) decltype((a->x)) x4 = x3; // type of x4 is const double& (lvalue expression) auto f = [](int a, int b) -> int { return a*b; }; decltype(f) f2 = f; // the type of a lambda function is unique and unnamed
9.1.5 pair
std::makepair
template <class T1,class T2> pair<T1,T2> make_pair (T1 x, T2 y) { return ( pair<T1,T2>(x,y) ); }
for example:
std::make_pair("hello", "world");
equals to:
std::pair<string, string>("hello", "world");
9.2 constant
Prefer const
, enum
, and inline
to #define
.
9.2.1 Rationale
- prefer the compiler to preprocessor,
the define may never be seen by compiler, thus less meaningful debug information, less optimization.
#define
don't respect scope.
9.2.2 How to use
9.2.2.1 const
const char* const name = "Hebi Li"; const std::string name("Hebi Li"); class A { private: static const int num = 5; };
- data and pointer const
char name[] = "Hebi Li"; char *p = name; // non-const const char *p = name; // const data char* const p = name; // const pointer // data pointer const char* const p = name; // double const
- const return value of operator
const A A::operator*(const A& lhs, const A& rhs); if (a*b = c) ... // ERROR assign c to a*b
char& B::operator[](std::size_t position); B b[]; b[0] = 'x'; // need & in return value, or this assignment can't work because assign to a char
9.2.2.2 enums
Some compilers don't support to init value at definition, because they insist they need to get the when compiling the class. In this case, use enum hack:
class A { private: enum {Num = 5}; // the enum hack: make Num a symbolic name for 5 };
9.2.2.3 inlines
replace
#define CALL_WITH_MAX(a,b) f((a) > (b) ? (a) : (b))
with
template<typename T> inline void callWithMax(const T& a, const T& b) { f(a>b?a:b); }
Because you need worry about the parenthesize for define:
int a=5,b=0; CALL_WITH_MAX(++a, b); // a increased twice CALL_WITH_MAX(++a, b+10); // a increased once
9.3 undefined behavior
int *p = 0; // null pointer std::cout << *p; // UNDEFINED dereferencing a null pointer char name[] = "Carla"; char c = name[10]; // UNDEFINED invalid array index
They most come from pointer and address.
9.4 Best Practices
9.4.1 compilation dependence
The change of a single class can lead to a large amount of file to recompile, because:
- Inheritance
- Use another class inside a class
9.4.1.1 Forward-declaration doesn't work.
int main() { int x; Person p(params); }
Forward-declaration cannot make it because this is a define, compiler need to know the size.
9.4.1.2 Why Java don't have such problem?
Java treat the above code as
int main() { int x; Person * p; }
9.4.1.3 Solution 1: pimpl(Pointer to implementation)
In C++, we can of course play the "hide the object implementation behind a pointer" game ourself.
The key: replacement of dependencies on definitions with dependencies on declarations.
- avoid using objects when object references and pointers will do
- depend on class declarations instead of class definitions whenever you can
Note: you never need a class definition to declare a function using that class, not even if the function passes or returns the class type by value:
class Date; Date today(); void clearAppointment(Date d);
Because if anybody calls those functions, Date's definition must have been seen prior to the call. So it is not that nobody calls them, it's that not everybody calls them.
- provide separate header flies for declarations and definitions
Classes that employ the pimpl idiom are often called Handle Classes.
- Example:
#include <string> #include <memory> class PersonImpl; // forward decl class Date; class Address; class Person { public: Person(const std::string& name, const Date& birthday, const Address& addr); std::string name() const; std::string birthDate() const; std::string address() const; private: std::shared_ptr<PersonImpl> pImpl; };
#include "Person.h" // we need include PersonImpl.h in order to call the member function // PersonImpl has exactly the same API #include "PersonImpl.h" Person::Person(const std::string& name, const Date& birthday, const Address& addr) : pImpl(new PersonImpl(name, birthday, addr)) {} std::string Person::name() const { return pImpl->name(); }
9.4.1.4 Solution 2: Interface Class
The implementation of non-virtual functions should be the same for all classes in a hierarchy, so it makes sense to implement such functions as part of the Interface class.
class Person { public: virtual ~Person(); virtual std::string name() const = 0; virtual std::string birthDate() const = 0; virtual std::string address() const = 0; static std::shard_ptr<Person> create(const std::string& name, const Date& birthday, const Address& addr); }; std::shared_ptr<Person> create(const std::string& name, const Date& birthday, const Addrss& addr) { return std::shared_ptr<Person>(new RealPerson(name, birthday, addr)); }
class RealPerson : public Person { public: RealPerson(const std::string& name, const Date& birthday, const Address& addr) : theName(name), theBirthDate(birthday), theAddress(addr) {} virtual ~RealPerson() {} std::string name() const; // implement std::string birthDate() const; std::string address() const; private: std::string theName; Date theBirthDate; Address theAddress; };
Clients of interface class need not recompile unless the Interface class's interface is modified.
9.5 coding standards
9.5.1 <xxx> and <xxx.h>
C++ standard library is guaranteed to have 18 standard headers from C.
Two type of names: <cxxx>
and <xxx.h>
<cxxx>
: provide in thestd
namespace only<xxx.h>
: make them available in bothstd
and global. Deprecated
9.5.2 using
- using-directive:
using namespace std;
. Do not use. - using-declaration:
using std::cout;
. Can be used just as a statement, e.g. in a function.
9.5.3 where to declare variables
Declare near the first use.
If you don't have enough information to initialize an object until the middle of the code, create it there. Don't initialize it to empty and reassign it later, because performance.
9.5.4 some lint-like guidelines
- A class
Fred~’s assignment operator should return ~*this
as aFred&
(allows chaining of assignments) - A class with any virtual functions ought to have a virtual destructor
- A class with any of the following generally needs all 5
- destructor
- copy assignment operator
- copy constructor
- move assignment operator
- move constructor
- A class
Fred~’s copy constructor and assignment operator should have const in the parameter: respectively ~Fred::Fred(const Fred&)
andFred& Fred::operator= (const Fred&)
- When initializing an object’s member objects in the constructor, always use initialization lists rather than assignment. 3x performance.
- Assignment operators should make sure that self assignment does nothing, otherwise you may have a disaster
9.5.5 some crazy unix abbr
abbr evthng n sght, usng vry shrt idntfr nms
10 C++ Standards
10.1 C++11
10.1.1 Default and Delete
The common idiom of "prohibiting copying" can now be expressed directly:
class X { // ... X& operator=(const X&) = delete; // Disallow copying X(const X&) = delete; };
Conversely, we can also say explicitly that we want to default copy behavior:
class Y { // ... Y& operator=(const Y&) = default; // default copy semantics Y(const Y&) = default; };
The "default" mechanism can be used for any function that has a default. The "delete" mechanism can be used for any function. But, just use them on copy constructor and assignment operator.
11 Trouble Shooting
11.1 RTTI
use -fno-rtti
to NOT use rtti. Otherwise error: undefined
reference to typeinfo for xxx.
12 Operator Precedence
Precedence | Operator | Description | Associativity |
---|---|---|---|
0 | :: | scope resolution | L to R |
1 | ++ -- | Suffix increment and decrement | |
() | Function call | ||
[] | Array subscripting | ||
. | Structure and union member access | ||
-> | Structure and union member access through pointer | ||
(type){list} | Compound literal(C99) | ||
2 | ++ -- | Prefix increment and decrement | R to L |
+ - | Unary plus and minus | ||
! ~ | Logical NOT and bitwise NOT | ||
(type) | Type cast | ||
* | dereference | ||
& | Address-of | ||
sizeof | |||
_Alignof | Alignment requirement(C11) | ||
new, new[] | Dynamic memory allocation | ||
delete, delete[] | Dynamic memory deallocation | ||
3 | * / % | L to R | |
4 | + - | Addition and subtraction | |
5 | << >> | Bitwise left shift and right shift | |
6 | < <= | Compare | |
> >= | |||
7 | = ! |
||
8 | & | Bitwise AND | |
9 | ^ | Bitwise XOR (exclusive or) | |
10 | l | Bitwise OR (inclusive or) | |
11 | && | Logical AND | |
12 | ll | Logical OR | |
13 | ?: | Ternary conditional | R to L |
14 | throw | ||
= | |||
+= -= | |||
*= /= %= | |||
<<= >>= | Assignment by bitwise left shift and right shift | ||
&= ^= l= | Assignment by bitwise AND, XOR, and OR | ||
15 | , | Comma | L to R |
12.1 notes
12.1.1 For ?:
the middle of the conditional operator (between ? and :)
is parsed as if parenthesized: its precedence relative to ?:
is ignored
12.1.2 For C++
The operand of sizeof can't be a C-style type cast:
the expression sizeof (int) * p
is unambiguously interpreted as (sizeof(int)) * p
,
but not sizeof((int)*p)
.