Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383
Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383
Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383
Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383
Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383
Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383
非常容易混淆的三种语法,在新手看来含义似乎相同,都是在函数调用上参数的传递,但实际上有非常大的区别。下面我们来具体分析分析。
首先是&&右值引用,在说这个之前先得弄清什么是右值。右值也叫将亡值(这翻译简直(¬_¬)),顾名思义也就是说将会死亡的值。比如类似这样的代码:
1 | std::string s = std::string("abc"); |
从原理上来说,这句代码的含义是,首先在等号右侧构建一个临时字符串对象,然后将右侧对象执行拷贝构造,拷贝至变量s中,然后释放等号右侧对象(实际的实现中,编译器通常会对这样的代码进行优化,直接在变量s中构建字符串对象)。
如果上面的例子不太明显那么再来一个例子,Gdi+中经常会出现这样的代码:
1 2 | Gdiplus::Graphics g(&img); g.DrawImage(&img2, &Gdiplus::Rect(0, 0, 10, 10), ...); |
似曾相识的代码对吧?这儿的DrawImage函数在执行前首先构造匿名Gdiplus::Rect对象,然后作为参数传递,函数调用返回时,也就是这行代码执行完时,匿名对象将被释放。这也是它被叫做将亡值的原因。
既然这种对象在使用时生命周期短,拷贝至左值又可能会影响效率,那有没更好的引用方式呢?当然有,这叫右值引用。
1 2 3 4 5 6 7 8 9 | #include <iostream> int main (int argc, char* argv []) { int &&i = std::move (1); std::cout << i << std::endl; i = 2; std::cout << i << std::endl; return 0; } |
代码运行结果为1、2。原理就是,通过std::move移动语义,将右值“1”的寿命延长,在当前行执行完之后还可以继续使用,寿命在引用变量释放时结束。右值引用也可以传递,不过需要注意的是,一旦传递之后,原始变量就不能使用这个对象了。
另外需要注意的是,移动语义的引用不能放在当前函数块之外继续使用。比如以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #include <string> using namespace std; class A { public: A () { cout << "new object" << endl; } ~A () { cout << "delete object" << endl; } }; A&& func () { return move (A ()); } int main (int argc, char* argv []) { A&& a = func (); cout << "function end" << endl; return 0; } |
代码执行结果为:
由此可见,对象虽然返回了右值引用,但在进入main作用域前就已经被释放掉了。所以这东西在栈内存上酌情使用。
下面我说说std::ref这个东西,顾名思义,引用,不过这个东西和普通的参数引用有什么区别呢?
1 2 3 4 5 6 7 8 9 10 11 | #include <iostream> #include <functional> int main (int argc, char* argv []) { auto f = [] (int &i) { i += 1; }; int x = 1; auto f2 = std::bind (f, x); f2 (); std::cout << x << std::endl; return 0; } |
如果遇到了这样的代码,可能有些人就完全找不着北了。lambda使用引用参数,然后将其与变量x绑定,讲道理结果应该为2啊,但很神奇的,结果为1。产生这个神奇现象的原因是,std::bind内部构造的问题。std::bind通常作用域多线程领域,假如需要一个std::string参数,传递的如果不是左值,那么很容易导致程序崩溃。这时候就将其拷贝一份,虽然牺牲点效率但总比出现让人摸不着头脑的bug要好。
那对于引用参数绑定如何传递呢?解决方法是使用std::ref关键字,对其引用传递,将std::bind那行代码改为如下形式:
1 | auto f2 = std::bind (f, std::ref (x)); |
这样之后,变量就能引用传递了。不出所料程序运行结果为2。
最后剩下的这个,智能指针,它主要用于自动回收内存,具体实现参考 C++11:智能指针。
讲得不错,非常非常好
又一两个月缺更了
工作太忙(T_T)
分享的不错,谢谢
感觉正讲到火候突然断了,有几个问题
- 多线程传递引用是否可以用std::bind 取代std::ref
- std::ref是否返回的是右值引用,是否能够传入将亡值
1、std::bind用于函数及参数的绑定,std::ref用于传参时传递变量引用,所以不能。2、std::ref主要目的是将闭包内的修改后的参数变量通过引用给传递出去,但右值移走之后变量就不能再使用了,所以无论如何也不会有这种需求。