Reference:
https://www.stroustrup.com/C++11FAQ.html
Contents
nullptr keyword. Shall be used.
+ Can't be used as an integral value (more typesafe than older ways)
const char *p = nullptr;
enum classes. Shall be used.
+ Enum classes doesn't implicitly convert to int, avoiding errors caused when someone does not want an enumeration to act as an integer. + Enum classes doesn't export their enumerators to the surrounding scope, avoiding name clashes. + Enum classes underlying type can be specified. + Enum classes can be forward declared
// Example taking from Bjarne explaining enum classes enum Alert { green, yellow, orange, red }; // traditional enum enum class Color { red, blue }; // scoped and strongly typed enum // no export of enumerator names into enclosing scope // no implicit conversion to int enum class TrafficLight { red, yellow, green }; Alert a = 7; // error (as ever in C++) Color c = 7; // error: no int->Color conversion int a2 = red; // ok: Alert->int conversion int a3 = Alert::red; // error in C++98; ok in C++11 int a4 = blue; // error: blue not in scope int a5 = Color::blue; // error: not Color->int conversion Color a6 = Color::blue; // ok
Deleting special member functions Shall be used.
+ Keyword ' = delete' disables the use of: Default constructor, Destructor, Copy constructor, Copy assignment operator=, move Constructor or Move operator= Example of usage: * remove default constructor when class must be initialized by parameters during construction (for clarity & safety of change). * prevent copying and assignment of "non copyable" classes.
class C { C() = deleted; C(const C&) = deleted; C &operator=( const C&) = deleted; };
See similar topic: Defaulted special member functions
Override controls Shall be used.
+ Keyword override allows compiler to check if there's an appropriate virtual method to override. Previously changing a virtual method signature silently broke all overrides + Keyword final prevents further overriding of virtual methods or inheriting of classes
// See Bjarne explain override and final class Base { public: virtual void MyMethod (); }; class MyClass : public Base { public: // override means compiler will generate compiler error if // there's no Base::MyMethod with matching signature void MyMethod () override; }; class MyOther Class : public Base { public: // final prevents further overrides of MyMethod void MyMethod () final; }; // final prevents DoNotInheritMe from being inherited class DoNotInheritMe final { };
Smart pointers. Shall be used.
+ Robust life-time management of heap objects (ie new:ed objects) + Supports reference counting + Supports weak pointers (useful for object graphs) + Doesn't require any special base-class for shared objects + std::make_shared only does one heap allocation (weak reference counting typically requires two heap allocations) + Can create std::shared_ptr from this using std::enable_shared_from_this + Deprecates std::auto_ptr (std::auto_ptr was useful but odd)
// See Bjarne explain: // 1. shared pointers // 2. weak pointers // 3. unique pointers class MyClass { std::shared_ptr<std::string> m_shared; public: MyClass (const std::shared_ptr<std::string> & shared) : m_shared (shared) { } void DoStuff (); }; auto shared_string = std::make_shared<std::string> ("A shared string"); auto shared_class = std::make_shared<MyClass> (shared_string); shared_string.reset (); // Clears this pointer but since string is referenced // by MyClass instance it's not deleted if (shared_class) // Test for non-empty pointers shared_class->DoStuff (); shared_class.reset (); // Clears this pointer which deletes the MyClass instance // this implicitly clears m_shared which deletes the string value
Auto, typeless declaration. Know about this
- Use a variable name that tell what the instance is (if it is known in the context).
- Use auto only if eclipes can deduce the type. It shall be possible to be 100% sure of the type. This unfortuatly removes complicated auto usage.
+ reduces the dependency towards the declared types and allows for typechange without code modification. + removes extensive typedeclarations that clutter the code and take focus from what the code really does. - hides the type of the variable. Example: if it is changable (const or not). - forces usage "advanced" tools (eclipse, ...) to know the type. - require disabling of alot of lint checks
The lint errors wary depending on how the variable is used, Example:
/*lint -e808 -e64 -e55 -e58 -e48 -e10 -e118 -e1013 -e40 -e1055 -e746 */
- How and when to us auto depends on circumstances, see Examples and Guidelines for other c++11 features.
Uniform initialization Know about this
Automatically deduce type of object to construct
+ Remove the need for redundant typename/constructor.
At return:
Point DoubleHeight() { return { x, y, z*2 }; // old style needs a specific constructor 'return Point(x, y, z*2 );' }
return {}; // return a empty list regardless of type
At object list-initialization:
std::vector<Point> listOfMirrorOnPlanes {{ x, y, 0 }, [x, 0, z}, {0, y, z}};
In functions arguments
float distance(Point p); ....... auto length = distance( { x, y, z } );
Range Based For Loops Know about this
Prefered usage:
for (const auto& instance : container) { instace.function(); } const to ensute that nothing changes (sequrity) auto to allow remove dependency of type (allow change of design) & to avoid copy of instance (efficiency)
//lint ....
const iterators Know about this
- cbegin
- cend
+ Use const type while iterating to sequre against modification. - Requires disabling a lot of lint rules
/*lint -e1013 -e1055 -e48 -e10 -e40 -e118 (C++11)*/
for (auto connection = m_connectionMapT.cbegin(); connection != m_connectionMapT.cend(); ++connection) { if( connection->second.getRemoteConnectionRef() == remoteConnectionRef && connection->second.getRemotePid() == remotePid ) { localConnectionRef = connection->first; return true; } } return false;
initializer lists Know about this
+ Easy initialization (very useful for data driven tests) - lint warnings
//lint !e64 (C++11)
// See Bjarne explain initializer lists std::vector<U32> procedureRefIdentifiers = {RBS_IW_CGCI_CONN_ESTABLISH_REQ};//lint !e64 !e838 (C++11) // Initializing test data for PM // pmMoClassCounterDefinitions_t is a vector of ClassCounterDefinitions const pmMoClassCounterDefinitions_t standardClassCounterDefinitions = { { MoClassId_1 , // moClassId MOCLASSNAME_1 , // moClassName MaxNoOfInstances_MOC1 , // maxNoOfMoInstances // Initializes a vector of counter definitions { { CounterId_1 , // counterId COUNTERNAME_1 , // counterName PmCollectionMethod::CC , // collectionMethod PmCounterAggregationMethod::Sum , // counterAggregationMethod false , // compressedFormatSupported PmCounterSize::_64Bit , // counterSize true , // resetCounterAtEOR 10 , // initialValue 1 , // multiplicity }, }, }
Lambda expressions Know about this
+ Simplifies use of C++ algorithms (<algorithm>) + Supports capture of values and references + Mostly deprecates the need for functional objects + Enables functional-style programming - Syntax can feel odd in the beginning
// See Bjarne explain lambda expressions // Basically lambda expressions allows a developer to define an inline function std::vector<int> data = get_data (); auto find_5 = std::find_if ( data.begin () , data.end () , // An inline function that takes an int // returns true if input is equal to 5 [] (int i) { return i == 5; } ); auto x = 3; auto find_x = std::find_if ( data.begin () , data.end () , // An inline function that takes an int // returns true if input is equal to x // as x needs to be captured we have to list // x is in the capture list ([x]) [x] (int i) { return i == x; } ); // Lambdas can be handled as values (functional style) auto test_x2 = [x] (int i) {return i == x;}; auto otest_x2= test_x2; auto find_x2 = std::find_if (data.begin (), data.end () , test_x2); // Lambdas can also be stored as std::function<>, useful // if you want to save the lambda as member or return from function std::function<bool (int i)> test_x3 = [x] (int i) {return i == x;}; // Note; the C++ compiler can't optimize this as heavily since // std::function relies on virtual dispatch which hides information // from the compiler auto find_x3 = std::find_if (data.begin (), data.end () , test_x3); return test_x3;
Raw string literals Know about this
+ Simplifies writing strings with embedded whitespace characters (\n,\r ...) + Flexible prelude and epilogue makes it possible to avoid escaping
// See Bjarne explain raw string literals // Without raw string literals: auto some_regex = "('(?:[^\\\\']|\\\\.)*'|\"(?:[^\\\\\"]|\\\\.)*\")|"; auto quoted_string = "\"quoted string\""; // With raw string literals (note the R): auto rquoted_string= R"("quoted string")"; // Prints: "quoted string" printf ("%s\n", rquoted_string)
Control of defaults Know about this Shall be used
+ Keyword default allows creation of default implementation of ctors/assignment operators + Keyword delete allows creation of default implementation of ctors/assignment operators + Keyword delete allows creation of deleted methods (used to prevent errornous overloads)
// See Bjarne explain default and delete class MyDefaultConstructibleClass { public: // This supresses the default constructor MyDefaultConstructibleClass (int i); // Use default to unsupress it MyDefaultConstructibleClass () = default; }; class MyNonCopyableClass { public: // Prevents default copy MyNonCopyableClass (const MyNonCopyableClass&) = delete; MyNonCopyableClass& operator= (const MyNonCopyableClass&) = delete; }; class MyMoveableClass { public: // Support default move (also implicitly prevents copying) MyMoveableClass (MyMoveableClass&&) = default; MyMoveableClass& operator= (MyMoveableClass&&) = default; }; class MyClass { public: // Support default move & copy MyClass (const MyClass&) = default; MyClass& operator= (const MyClass&) = default; MyClass (MyClass&&) = default; MyClass& operator= (MyClass&&) = default; }; // A template method that does something template<typename T> void MyMethod (T v); // Deleted methods to prevent calling MyMethod with C-strings // Will be part of overload resolution and generate compiler errors void MyMethod (const char *) = delete; void MyMethod (const wchar_t *)= delete;
Static assert Know about this
+ Compile-time asserts + Useful for checking invariants that are known during compile-time such as sizeof (4 == sizeof (int)
// See Bjarne explain asserts static_assert (sizeof (int) == 4, "ints must be 4 bytes wide"); static_assert (sizeof (long) == 4, "longs must be 4 bytes wide"); template<typename T> void Method (T&& v) { static_assert (std::is_base_of<Base, T>::value, "T must inherit Base"); // Do stuff }
RValue references Know about this
+ Allows overloading on "temporary" objects, this allows for optimizations + Is easy to benefit from, you gain performance benefits by recompiling code + Allows move semantics for performance and objects that can be moved but not copied + There are good patterns when implementing classes that should support RValue references - RValue references are difficult to fully understand
// See Bjarne explain rvalue references // See Scott explain universal references std::string x,y,z,w,u; // Initialize x,y,z,w,u with values // In C++98: Creates 3 unnecessary string copies // In C++11: Doesn't create unnecessary string copies // We benefit by just recompiling the code auto result = x + y + z + w + u; class MyNamedMutex { std::string m_name; public: MyNamedMutex (std::string name) // Moves the content of name into m_name to avoid // unnecessary copies : m_name (std::move (name)) { } // A named mutex can't be copied, but they can be moved MyNamedMutex (MyNamedMutex &&); }; MyNamedMutex CreateMutex (); // Creates a mutex and moves it the calling context auto mutex = CreateMutex (); std::vector<int> ReturnsBigData (); // In C++98: Potentially creates an extra copy of BigData, pray for RVO // In C++11: Never creates an extra copy of BigData auto big_data = ReturnsBigDate ();
default member initialization (of non-static members).
+ derived classes becomes less dependent of base class implementation.
Note that constructor initializaer list override this (the old way to initialize members).
class C { private: int x = 5; std::string id = { defaultID() }; };
Templates using c++11 features
...
= links =
Index | Comment | Legacy C/C++ | C++ |
1 | String handling | const char* strcmp, strdup, strcpy … | std::string |
2 |
| struct | class |
3 | Pointers(尽量使用智能指针) | * malloc, free, memcpy
| & std::shared_ptr std::weak_ptr std::unique_ptr |
4 | Arrays(使用STL) | new [] /delete [] | std::vector std::array |
5 | String formatting | printf, sprintf and similar | std::ostream << |
6 | Enumeration(使用enum class) | enum | enum class |
7 | Function pointer | C style function pointer. SLOT | std::function |
8 | Type casting. This should be avoided and should rarely be needed. | C style type casting | static_cast, dynamic_cast, const_cast. |
9 | Allocation of dynamic data. (note, consider using smart pointers for controlling the lifecycle of objects instead). | malloc, free | new,delete |
10 |
| typedef | using |
11 | Looping over a container. | iterators | range-based for loop |
12 |
| NULL, NIL | nullptr |
13 | Data initialization | = | {} this gives stronger type check |
Smart pointers
From <https://en.cppreference.com/w/cpp/memory>
Smart pointers enable automatic, exception-safe, object lifetime management.
Defined in header <memory>
- std::shared_ptr
template< class T > class shared_ptr;
(since C++11)
包含共享得个数和对象
std::shared_ptr is a smart pointer that retains shared ownership of an object through a pointer. Several shared_ptr objects may own the same object. The object is destroyed and its memory deallocated when either of the following happens:
- the last remaining shared_ptr owning the object is destroyed;
- the last remaining shared_ptr owning the object is assigned another pointer via operator= or reset().
The object is destroyed using delete-expression or a custom deleter that is supplied to shared_ptr during
Usage of Smart Pointers(8.5)
Using of smart pointers is encouraged for avoiding problems with controlling the lifecycle of dynamically allocated objects. These rules apply:
- The raw C pointer shall never be stored from a smart pointer.
- For callbacks, std::weak_ptr should normally be used to avoid circular pointer references.
- For objects accessed by one object, std::unique_ptr should be used, otherwise std::shared_ptr should be used.
- std::auto_ptr is deprecated and shall not be used – std::unique_ptr should be used instead.
- When creating objects owned by smart pointers, these should preferably be created by std::make_unique resp std::make_shared
In function signatures, the following applies:
- use a raw reference if the function is just using the pointer and not altering its ownership -- i.e. not storing it/replacing it
- if the function is consuming the object: use std::unique_ptr by value
- if the function is taking shared ownership: use std::shared_ptr by value
- if the function will reseat the pointer (replace it): use std::unique_ptr / std::shared_ptr by reference
For more information on principles for using smart pointers, please read the good rules defined in reference (which contains examples and motivations):
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rr-owner
Enum class/enum struct
- 作用域:enum只能用namespace来解决重名问题
https://blog.youkuaiyun.com/thinkerleo1997/article/details/80355905
std::function(2.32)
From <https://blog.youkuaiyun.com/acoolgiser/article/details/90705650>
但是采用模板最大的问题在于编译期展开,头文件会变得很大,编译时间也会很长。
C++11引入std::function更好的解决了这一问题。
std::function可以用于保存并调用任何可调用的东西,比如函数、lambda函数、std::bind表达式、仿函数,甚至是指向对象成员的指针。
std::function简单来说就像是个接口,且能够把符合这个接口的对象(这里对象泛指一切类型,并非面向对象编程中的对象)储存起来,更神奇的是,两个std::function的内容可以交换。
uniform initialization(标准初始化)以及initializer_list(2.14)
在c++11中就统一了初始化的形式——“{ }”,直接在对象名后面跟大括号,并在大括号中写入需要初始化的值,并用逗号隔开。甚至类构造函数的初始化列表也可以用“{}”大括号
- int values[] {1,2,3,4};
- vector<int> v {1,2,3,4};
- complex<double> c{3.0,4.0};
Bad example:
int pi = 3.14; // no compilation error, i truncated to 3
Better example:
double pi {3.14}; //compilation error if pi is not float or double
initializer_list<>
上面所说的标准初始化格式,并不仅仅是形式上的统一,而是在背后有一个更加复杂的机制。编译器会将大括号里面的内容({1,2,3,4})编译成一个initializer_list<>,我们可以把它理解为一个模板容器(其背后的机制是array,但是我们这里不做探讨)。这时候编译器就会调用形参是initializer_list的函数,或者是构造函数。如果没有找到对应的形式,就会将initializer_list分解为单个的元素,进行匹配。
Take advantage of type definitions defined by STL containers:
When using STL containers, avoid duplication in code by creating a type definition for the instantiation of the container template, and using the provided type definitions such as value_type, reference, const_reference, and so forth.
Example:
class MyClass
{
using FrequencyList = std::vector<Frequency>;
FrequencyList m_List;
FrequencyList::const_reference getEntry(…) const;
};
member type | definition | notes |
---|---|---|
value_type | The first template parameter (T) | |
allocator_type | The second template parameter (Alloc) | defaults to: allocator<value_type> |
reference | value_type& | |
const_reference | const value_type& | |
pointer | allocator_traits<allocator_type>::pointer | for the default allocator: value_type* |
const_pointer | allocator_traits<allocator_type>::const_pointer | for the default allocator: const value_type* |