欢迎使用CSDN-markdown编辑器

来源:互联网 发布:燃料420走淘宝 编辑:程序博客网 时间:2024/05/21 06:02

C++11 特性

Including
Non-throwing swap idiom
Copy-and-swap idiom
Move assignment operator
std::move
const & 常量引用
右值引用


Non-throwing swap idiom

传统swap无法应对资源情况。这里采用交换指针。
swap参数为指针
temp.swap (*this)
http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-throwing_swap

Copy-and-swap idiom

There are at least 3 types of exception safety levels: basic, strong, and no-throw.
http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Copy-and-swap

String & operator = (const String &s){   String temp (s); // Copy-constructor -- RAII   temp.swap (*this); // Non-throwing swap   return *this;}// Old resources released when destructor of temp is called.T& T::operator=(T arg) {                 // copy/move constructor is called to construct arg    swap(arg);    // resources exchanged between *this and arg    return *this;}  // destructor is called to release the resources formerly held by *this

For non-throwing swap(), this form provides strong exception guarantee. For rvalue arguments, this form automatically invokes the move constructor, and is sometimes referred to as “unifying assignment operator” (as in, both copy and move).

 // non-copy-and-swap assignment   C& operator=(const C& other)

Move assignment operator

Move assignment operators typically “steal” the resources held by the argument (e.g. pointers to dynamically-allocated objects, file descriptors, TCP sockets, I/O streams, running threads, etc), rather than make copies of them, and leave the argument in some valid but otherwise indeterminate state. For example, move-assigning from astd::string or from a std::vector leaves the right-hand side argument empty.

如果X是一种类型,那么X&&就叫做X的右值引用
Both

T& T::operator=(const T&&)
T& T::operator=(T&&)

~D() {}; // destructor would prevent implicit move assignmentD& operator=(D&&) = default; // force a move assignment anyway

http://en.cppreference.com/w/cpp/language/move_operator

std::move

std::move obtains an rvalue reference to its argument and converts it to an xvalue.标准库提供了一个 std::move 函数,用来把一个左值转换为一个右值.

// Simple move constructor   A(A&& arg) : member(std::move(arg.member)) // the expression "arg.member" is lvalue   {}    // Simple move assignment operator   A& operator=(A&& other) {        member = std::move(other.member);        return *this;   }   // uses the push_back(const T&) overload, which means        // we'll incur the cost of copying str       v.push_back(str);   // uses the rvalue reference push_back(T&&) overload,        // which means no strings will be copied; instead, the contents       // of str will be moved into the vector.  This is less       // expensive, but also means str might now be empty.       v.push_back(std::move(str));

Notes
If both copy and move assignment operators are provided, overload resolution selects the move assignment if the argument is an rvalue (either prvalue such as a nameless temporary or xvalue such as the result of std::move), and selects the copy assignment if the argument is lvalue (named object or a function/operator returning lvalue reference). If only the copy assignment is provided, all argument categories select it

C++11中的swap函数是这样的:

template<class T> void swap(T& a, T& b) {   T tmp(std::move(a));  a = std::move(b);   b = std::move(tmp);} X a, b;swap(a, b);

我们可以在一些之前不可复制的类型不被允许的情况下,用一些不可复制但是可以移动的类型(unique_ptr)。这样的类型是可以作为容器元素的。

const & 常量引用

C++常量引用语法上可以引用一个临时变量。
关于引用的初始化有两点值得注意:
(1)当初始化值是一个左值(可以取得地址)时,没有任何问题;
(2)当初始化值不是一个左值时,则只能对一个const T&(常量引用)赋值。而且这个赋值是有一个过程的:
首先将值隐式转换到类型T,然后将这个转换结果存放在一个临时对象里,最后用这个临时对象来初始化这个引用变量。在这种情况下,const T&(常量引用)过程中使用的临时对象会和const T&(常量引用)共存亡。
例子:

double&  dr  =  1 ; // 错误:需要左值  const double&  cdr  =  1 ; // ok 

第二句实际的过程如下:

double  temp  =  double (1);  const double&  cdr  =  temp ; 

右值引用 T&&

一个表达式有左值(lvalue)和右值(rvalue)之分,即是否可寻址之分。
一般的变量都属于左值,而像(1 + 2)这种表达式的结果,则是不可寻址的,属于右值。
可改变的左值,被 & 绑定;不可寻址的值自然无法改变(或者说,不应该被改变),则会被 const & 绑定。
在C++11里,表达式可以被分为3类:左值、右值、xvalue。其中 xvalue 是右值引用表达式的值,它可左可右。
把复杂的概念抛开,xvalue 可以被看做一个临时值,意思是马上就会被销毁的值。
右值引用和左值引用的行为差不多,但是有几点不同,最重要的就是函数重载时左值使用左值引用的版本,右值使用右值引用的版本:

void foo(X& x); // 左值引用重载void foo(X&& x); // 右值引用重载X x;X foobar();foo(x); // 参数是左值,调用foo(X&)foo(foobar()); // 参数是右值,调用foo(X&&)

这样的重载只出现在拷贝构造函数和赋值运算符中,以用来实现move语义:

X& X::operator=(X const & rhs); // classical implementationX& X::operator=(X&& rhs){  // Move semantics: exchange content between this and rhs  return *this;}

如果你实现了void foo(X&);,但是没有实现void foo(X&&);,那么和以前一样foo的参数只能是左值。如果实现了void foo(X const &);,但是没有实现voidfoo(X&&);,仍和以前一样,foo的参数既可以是左值也可以是右值。唯一能够区分左值和右值的办法就是实现void foo(X&&);。最后,如果只实现了实现voidfoo(X&&);,但却没有实现void foo(X&);和void foo(X const &);,那么foo的参数将只能是右值。

右值引用类型既可以被当作左值也可以被当作右值,判断的标准是,如果它有名字,那就是左值,否则就是右值。

Example:Base(Base const & rhs); // non-move semanticsBase(Base&& rhs); // move semantics左值版本的拷贝构造函数很直白:Derived(Derived const & rhs)   : Base(rhs){  // Derived-specific stuff}但右值版本的重载却要仔细研究下Derived(Derived&& rhs)   : Base(rhs) // 错误:rhs是个左值{  // ...}

如果像上面这样写,调用的永远是Base的非move语义的拷贝构造函数。因为rhs有名字,所以它是个左值。但我们想要调用的却是move语义的拷贝构造函数,所以应该这么写:

Derived(Derived&& rhs)   : Base(std::move(rhs)) // good, calls Base(Base&& rhs){  // Derived-specific stuff}
0 0
原创粉丝点击