浏览量:364

C++11 note

list initialization

Initializes an object from braced-init-list.

	int list_initialization = { 100 };
	int list_initialization{ 100 };
	int list_initialization(100);
	int list_initialization = 100;

null pointers nullptr

	int * p = nullptr;

constexpr and constant expressions

A constant expression is an expression whose value cannot change and that can be evaluated at compile time. Variables declared as constexpr are implicitly const and must be initialized by constant expressions.

	constexpr int sz = 20;

type aliases

A type alias is a name that is a synonym for another type. An alias declaration starts with the keyword using followed by the alias name and an =.

typedef double DOUBLE;
using INT = int;

the auto type specifier

Under the new standard, we can let the compiler figure out the type for us by using auto type specifier.

auto i = 0, *p = &i;

the decltype type specifier

Sometimes we want to define a variable with a type that the compiler deduces from an expression but do not want to use that expression to initialize the variable. For such cases, the new standard introduced a second type specifier, decltype which returns the type of its operand. The compiler analyzes the expression to determine its type but does not evaluate the expression.

	const int ci = 0, &cj = ci;
	decltype(ci) x = 0; 	// x has type const int
	decltype(cj) y = x; 	// y has type const int& and is bound to x
	// decltype of a parenthesized variable is always a reference
	decltype((ci)) d; 		// error: d is const int& and must be initialized
	decltype(ci) e;		// ok: e is an (uninitialized) const int.

the library begin and end functions

The new library includes two functions, named begin and end. These functions act like the similarly named container members.

	int ia[] = { 0, 1, 2, 3, 4, 5, 6 };
	int *bg = begin(ia);
	int *ed = end(ia);

arithmetic operators

In a division, a nonzero quotient is positive if the operands have the same sign and negative otherwise. Earlier versions of the language permitted a negative quotient to be rounded up or down; the new standard requires the quotient to be rounded toward zero(i.e, truncated).

The modulus operator is defined so that if m and n are integers and n is nonzero, then (m/n)*n+m%n is equal to m.

	-21 % -8; 	/* result is -5*/ 		-21 / -8;		/* result is 2*/
	21 % 5; 	/* result is 1*/ 		21 / -5; 		/* result is -4*/

size_type type for vector, string etc.

It might be logical to expect that size returns an int or, or an unsigned. Instead, size returns a string(vector etc)::size_type.

	auto len = line.size()

range for statement

The new standard introduced a simpler for statement that can be used to iterate through the elements of a container or the sequence. The syntactic form of the range for statement is:

for( declaration : expression)

statement

expression must represent a sequence, such as a braced initializer list, an array, or an object of a type such as vector or string that has begin and end members that return iterators.

	vector<int> v = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	// range variable must be a reference so we can write to the elements.
	for(auto &r : v)
		r * = 2;

list initializing the return value

Under the new standard, functions can return a braced list of values. As in any other return, the list is used to initialize the temporary that represents the functions return.

vector<string> process(int expected){
		if (expected == 0)
	return{};
else if (expected == 1)
			return{ "functions" };
else
	return{ "functions", "ok" };
	}

inline functions

A function specified as inline(usually) is expanded “in line” at each call. The line specification is only a request to the compiler. The compiler may choose to ignore this request.

initializers for data members of class type

Under the new standard, the best way to specify this default value is as an in-class initializer.

class WindowMgr{
	private:
int UpperLimit = 100;
	};

We must use the constructor initializer list to provide values for members that are const, reference, or of a class type does not have a default constructor.

delegating constructors

The new standard extends the use of constructor initializers to let us define so-called delegating constructors. A delegating constructor uses another constructor from its own class to perform its initialization. It is said to “delegate” some (or all) of its work to this other constructor.

	class WindowData{
	public:
		WindowData():WindowData(100, 200) {}
		WindowData(int width, int height) :mWidth(width), mHeight(height) {}
	private:
		int mWidth, mHeight;
	};

lambdas expressions

link: http://en.cppreference.com/w/cpp/language/lambda

A lambda expression represents a callable unit of code. It can be thought of as an unnamed, inline function. Like any function, a lambda has a return type, a parameter list, and a function body. Unlike a function, lambdas may be defined inside a function. A lambda expression has the form:

[capture list] (parameter list) -> return type {function body}

For example,

	auto f1 = [] {
		return 42; 
	};
	auto f2 = [](const string& a, const string& b){
		return a.size() < b.size();
	};
	int sz = 100;
	auto f3 = [sz](const string& a){
		return a.size() >= sz;
	};
	auto f4 = [](int i) -> int{
		return i;
	};

A lambda capture stores information between the time the lambda is created (i.e., when the code that defines the lambda is executed) and the time (or times) the lambda itself is executed. It is the programmer’s responsibility to ensure that whatever information is captured has the intended meaning each time the lambda is executed.

When we capture a variable by reference, we must ensure that the variable exists at the time that the lambda executes.

As a rule, we can avoid potential problems with captures by minimizing the data we capture. Moreover, if possible, avoid capturing pointers or references.

binding arguments

link: http://en.cppreference.com/w/cpp/utility/functional/bind

We can solve the problem of passing a size argument to a function by using a new library function named bind, which is defined in the functional header. The bind function can be thought of as a general-purpose function adaptor. It takes a callable object and generates a new callable that “adapts” the parameter list of the original object.

#include <iostream>
#include <functional>
using namespace std::placeholders;

void f(int a, int b, int c, int d){
	std::cout << a << " " << b << " " << c << " " << d << std::endl;
}
int main(){
	auto g = bind(f, 1, _1, _2, 4);
	g(0, 100);
	return 0;
}

Using = default

We can explicitly ask the compiler to generate the synthesized versions of the copy-control members by defining them as = default.

	class SalesData{
	public:
		SalesData() = default;
		~SalesData() = default;
		SalesData(const SalesData&) = default;
	};

defining a function as deleted

Under the new standard, we can prevent copies by defining the copy constructor and copy-assignment operator as deleted functions.

	class SalesData{
	public:
		SalesData() = default;
		~SalesData() = default;
		SalesData(const SalesData&) = default;
		SalesData& operator=(const SalesData&) = delete;
	};

rvalue references and move function

To support move operations, the new standard introduced a new kind of reference, an rvalue reference. An rvalue reference is a reference that must be bound to an rvalue. An rvalue reference is obtained by using && rather than &.

Because rvalue references can only be bound to temporaries, we know that:1) The referred-to object is about to be destroyed; 2) There can be no other users of that object.

	int &&rr1 = 42; 		// ok: literals are rvalues
	int &&rr2 = rr1:		// error: the expression rr1 is an lvaue!
	int &&rr3 = std::move(rr1); 	// ok

move constructor and move assignment

link: http://en.cppreference.com/w/cpp/language/move_constructor

explicit conversion operators

In practice, classes rarely provide conversion operators. Too often users are more likely to be surprised if a conversion happens automatically than to be helped by the existence of the conversion.

To prevent such problems, the new standard introduced explicit conversion operators:

	class SmallInt{
	public:
		explicit operator int()const{
			return value;
		}
		int value;
	};
	SmallInt si = 3; 	// ok: the SmallInt constructor is not explicit.
	si + 3; 	// error: implicit is conversion required, but operator int is explicit.
	static_cast<int>(si) + 3; 		//ok: explicitly request the conversion

the final specifiers

Sometimes we define a class that we don’t want others to inherit from. Or, we might define a class for which we don’t want to think about whether it is appropriate as a base class. Under the new standard, we can prevent a class from being used as a base by following the class name with final.

	class NoDerived final {}; 
	class Base{};
	class Last final:Base{};
	class Bad: NoDerived{}; 	//error: NoDerived is final
	class Bad2: Last{};			// error: Last is final.

We can also designate a function as final. Any attempt to override a function that has been defined as final will be flagged as an error:

struct Base{
    virtual void foo();
};
struct A : Base
{
    void foo() final; // A::foo is overridden and it is the final override
    void bar() final; // Error: non-virtual function cannot be overridden or be final
};
 
struct B final : A // struct B is final
{
    void foo() override; // Error: foo cannot be overridden as it's final in A
};
 
struct C : B // Error: B is final
{
};

the override specifiers

Under the new standard we can specify override on a virtual function in a derived class. Doing so makes our intention clear and (more importantly) enlists the compiler in finding such problems for us. The compiler will reject a program if a function marked override does not override an existing virtual function.

struct A{
    virtual void foo();
    void bar();
};
struct B : A{
    void foo() const override; // Error: B::foo does not override A::foo
                         // (signature mismatch)
    void foo() override; // OK: B::foo overrides A::foo
    void bar() override; // Error: A::bar is not virtual
};

inline namespaces

the new standard introduced a new kind of nested namespace, an inline namespace. Unlike ordinary nested namespaces , names in an inline namespace can be used as if they were direct members of the enclosing namespace. That is, we need not qualify names from an inline namespace by their namespace name.

Smart pointers

smart pointer

To make using dynamic memory easier(and safer), the new library provides two smart pointer types that manage dynamic objects. A smart pointer acts like a regular pointer with the important exception that it automatically deletes the object to which it points. The new library defines two kinds of smart pointers that differ in how they manage their underlying pointers: share_ptr, which allows multiple pointers to refer to the same object, and unique_ptr, which “owns” the object to which it points. The library also defines a companion class named weak_ptr that is a weak reference to an object managed by a share_ptr. All three are defined in the memory header.

share_ptr:

http://en.cppreference.com/w/cpp/memory/shared_ptr

http://www.cplusplus.com/reference/memory/shared_ptr/

unique_ptr:

http://en.cppreference.com/w/cpp/memory/unique_ptr

http://www.cplusplus.com/reference/memory/unique_ptr/

weak_ptr:

http://en.cppreference.com/w/cpp/memory/weak_ptr

http://www.cplusplus.com/reference/memory/weak_ptr/

spacer

Leave a reply