移动语义到底什么用?没感觉有什么性能的提高。

李刚弄死他 2014-01-29 08:36:59

#include<iostream>
using namespace std;
class test
{
public:
test(){c=new char;*c='a';cout<<"构造函数执行...\n";}
~test(){delete c;cout<<"析构函数执行...\n";}
test(const test&r)
{
cout<<"复制构造函数执行...\n";
c=new char;
*c=*(r.c);
}
test(test&&r)
{
cout<<"移动构造函数执行...\n";
c=r.c;
r.c=nullptr;
}
private:
char *c;
};
test f()
{
return test();
}
int main()
{
test b=f();
return 0;
}

就是从调用复制构造函数换成了调用移动构造函数,调用次数还没变化。假如编译器做了优化,那么还要移动语义做什么用?
...全文
450 点赞 收藏 22
写回复
22 条回复
超度逗比 2015年07月03日
引用 5 楼 ALNG 的回复:
并不能保证所有时候都有性能上的提升,像你举例的情形,没有太大的定义一个移动构造函数的必要,性能上的提升维护其微。但是如果把你的例子稍稍变一下,相像你存的不是一个char, 而是一个array of char呢?移动时的代价不变,而赋值构造函数则必须把char 数组的内容复制一遍。如果这个char数组够大,比如1GB, 会节省多少时间? 在C++11之前,如果函数要返回一个std::string, std::vector<std::vector<std::vector<std::string> > >, 你没有办法不复制一份新(甚至2次或多次复制)的,再把旧的扔掉, std::vector<std::vector<std::vector<std::string> > >析构也还需要时间,那是后C++程序员能做的是不得不把返回值作为引用参数传递来避免这笔不必要的开销。 instead of

std::vector<std::vector<std::vector<std::string> > > some_function();
, use

std::vector<std::vector<std::vector<std::string> > >& some_function(std::vector<std::vector<std::vector<std::string> > >& modify_this_and_return_it);
这个常常意味着表达能力上的损失---代码读起来没有那么美。
楼主关注了一堆其他楼的回复,却不关注这位兄台的回复。该回复一举提及了“移动语义”的三个主要效用: 1. 一些场景下提升了性能; 2. 一些场景下提高了编码效率,相对没有利用移动语义的同效果其他设计方式来说,显得没有那么罗嗦、表达更自然; 3. 对应上面的第2点的场景,这样的编码方式自然可读性更强。
回复 点赞
珍惜生命远离CPP 2014年02月11日
引用 20 楼 u011774561 的回复:
[quote=引用 19 楼 akirya 的回复:]
struct test
{
	test()
	{
		puts("test::test");
	}
	test(const test& )
	{
		puts("test::test(const test& )");
	}
	test( test&& )
	{
		puts("test::test(test&&) ");
	}
};

test f( test&& v )
{
	return std::move(v);	
}
 
test f( const test& v )
{
	return v;
}


int main()
{
	f( f( f( test() ) ) );
}
一般来说移动构造的代价是小于等于拷贝构造的。 将 test f( test&& v ) 注释就明显看出来效率差距。 比如常用的std::string 效率能提升不小。
感觉提升不明显。[/quote] 省掉了一次内存分配和释放,内存分配释放是比较耗时的操作。
回复 点赞
李刚弄死他 2014年02月11日
引用 19 楼 akirya 的回复:
struct test
{
	test()
	{
		puts("test::test");
	}
	test(const test& )
	{
		puts("test::test(const test& )");
	}
	test( test&& )
	{
		puts("test::test(test&&) ");
	}
};

test f( test&& v )
{
	return std::move(v);	
}
 
test f( const test& v )
{
	return v;
}


int main()
{
	f( f( f( test() ) ) );
}
一般来说移动构造的代价是小于等于拷贝构造的。 将 test f( test&& v ) 注释就明显看出来效率差距。 比如常用的std::string 效率能提升不小。
感觉提升不明显。
回复 点赞
珍惜生命远离CPP 2014年02月09日
struct test
{
	test()
	{
		puts("test::test");
	}
	test(const test& )
	{
		puts("test::test(const test& )");
	}
	test( test&& )
	{
		puts("test::test(test&&) ");
	}
};

test f( test&& v )
{
	return std::move(v);	
}
 
test f( const test& v )
{
	return v;
}


int main()
{
	f( f( f( test() ) ) );
}
一般来说移动构造的代价是小于等于拷贝构造的。 将 test f( test&& v ) 注释就明显看出来效率差距。 比如常用的std::string 效率能提升不小。
回复 点赞
珍惜生命远离CPP 2014年02月09日
引用 17 楼 u011774561 的回复:
[quote=引用 16 楼 akirya 的回复:]
test f()
{
    return test();
}
这种直接返回值优化了,移动语义不是解决这种问题的。
void f( test&& v )
{
//可以调用v的非const成员函数
}

void f( const test& v )
{
//只能调用v的const成员函数
}
很多时候调用const成员函数是不够的 如果调用非const成员函数的话只能再拷贝构造一份对象,而使用移动语义的时候则不需要。
举例说明哪里可以用到移动语义。[/quote] 比较常用的就是返回值了,返回值必须构造一个对象,不管有没有返回值优化。这时候直接将&&参数移动过去就省掉一次拷贝构造的代价。
回复 点赞
李刚弄死他 2014年02月09日
引用 16 楼 akirya 的回复:
test f()
{
    return test();
}
这种直接返回值优化了,移动语义不是解决这种问题的。
void f( test&& v )
{
//可以调用v的非const成员函数
}

void f( const test& v )
{
//只能调用v的const成员函数
}
很多时候调用const成员函数是不够的 如果调用非const成员函数的话只能再拷贝构造一份对象,而使用移动语义的时候则不需要。
举例说明哪里可以用到移动语义。
回复 点赞
珍惜生命远离CPP 2014年02月07日
test f()
{
    return test();
}
这种直接返回值优化了,移动语义不是解决这种问题的。
void f( test&& v )
{
//可以调用v的非const成员函数
}

void f( const test& v )
{
//只能调用v的const成员函数
}
很多时候调用const成员函数是不够的 如果调用非const成员函数的话只能再拷贝构造一份对象,而使用移动语义的时候则不需要。
回复 点赞
unituniverse2 2014年02月03日
其实你要的答案#7楼基本都说清楚了。就比如你给的程序的例子,把

test(const test&r)
    {
        cout<<"复制构造函数执行...\n";
        c=new char;
        *c=*(r.c);
    }
改成

test(const test&r)
    {
        cout<<"复制构造函数执行...\n";
        c=new char [200000000];
        memset(c, 200000000, 0x0);
        *c=*r.c;
    }
再测试就可以观察到区别(别告诉我说你觉得memset是多余的,实际应用场景中的构造函数里面做的很多都比这个例子的多的多)。
回复 点赞
unituniverse2 2014年02月03日
引用 12 楼 u011774561 的回复:
说实话我也一直没看出移动语义有什么用,可能是标准委员会搞出的一个噱头吧。
发现你经常问了一些东西然后又说没用,有时候给你例子又选择性的无视。似乎你发这些帖的目的就是为了证明你问的这些东西“没多大用”似的
回复 点赞
李刚弄死他 2014年02月03日
引用 13 楼 unituniverse2 的回复:
[quote=引用 12 楼 u011774561 的回复:] 说实话我也一直没看出移动语义有什么用,可能是标准委员会搞出的一个噱头吧。
发现你经常问了一些东西然后又说没用,有时候给你例子又选择性的无视。似乎你发这些帖的目的就是为了证明你问的这些东西“没多大用”似的[/quote] 请看清楚题目,题目就说感觉没多大用,我要说有用,不是自己扇自己嘴巴吗?
回复 点赞
李刚弄死他 2014年02月02日
引用 10 楼 vanxining 的回复:
[quote=引用 2 楼 mougaidong 的回复:] 这不是move constructor的典型应用场景。在NRVO和RVO的情况下,copy constructor和move constructor 都可以被优化。也就是说move constructor在这种情况下(很可能)起不到什么作用,但是不代表它就没有作用。 照在我使用C++11的过程中个人体会来看,可以说,move语义的出现是使得C++从03版到11新版的核心推进力量。而move constructor 又是move语义得以贯彻施行的一个主要途径。 说了这么多,那么move constructor到底是为什么而生的,这其实还是要从move语义说起。move语义的本质之处,归纳起来也就一个字:“偷”。将一个右值对象中的堆内存通过指针的方式转移到另一个对象,则是偷的典型应用场景。 http://en.cppreference.com/w/cpp/language/move_constructor
这位朋友说了一大堆,似乎都没说到点上。 1、move语义只是C++ 11标准中提升运行时性能最重要的一个手段。然而究竟C++ 11中哪个新特性才是其“核心推进力量”,恐怕B.S.也无法说出个所以然来; 2、RVO(Return value optimization) == NRVO(Named return value optimization); 3、我无法看出楼主给出的例子和链接中的例子在原理上有什么差别。[/quote] 说实话我也一直没看出移动语义有什么用,可能是标准委员会搞出的一个噱头吧。
回复 点赞
李刚弄死他 2014年02月02日
引用 10 楼 vanxining 的回复:
[quote=引用 2 楼 mougaidong 的回复:] 这不是move constructor的典型应用场景。在NRVO和RVO的情况下,copy constructor和move constructor 都可以被优化。也就是说move constructor在这种情况下(很可能)起不到什么作用,但是不代表它就没有作用。 照在我使用C++11的过程中个人体会来看,可以说,move语义的出现是使得C++从03版到11新版的核心推进力量。而move constructor 又是move语义得以贯彻施行的一个主要途径。 说了这么多,那么move constructor到底是为什么而生的,这其实还是要从move语义说起。move语义的本质之处,归纳起来也就一个字:“偷”。将一个右值对象中的堆内存通过指针的方式转移到另一个对象,则是偷的典型应用场景。 http://en.cppreference.com/w/cpp/language/move_constructor
这位朋友说了一大堆,似乎都没说到点上。 1、move语义只是C++ 11标准中提升运行时性能最重要的一个手段。然而究竟C++ 11中哪个新特性才是其“核心推进力量”,恐怕B.S.也无法说出个所以然来; 2、RVO(Return value optimization) == NRVO(Named return value optimization); 3、我无法看出楼主给出的例子和链接中的例子在原理上有什么差别。[/quote] 你的意思是移动语义的作用甚微,提升性能是海市蜃楼?
回复 点赞
mujiok2003 2014年01月31日
1. RVO和NRVO不是强制要求 2. 有时候RVO和NRVO不适用

std::string a, b, c;
//....
std::string combine = a + "_" + b + "_" + c; //这里会好几个临时对象
3. 有些对象不时能拷贝的,但是可以move的 比如std::fstream, std::thread 4. move不是在所有场景下都立竿见影.
回复 点赞
encoderlee 2014年01月31日
简单来说把: 假如一个对象有一个int *num成员,这个成员指针指向一块new出来的内存num = new int[10000000]; 在没有移动语义的时候,如果这个对象从函数中return出来,函数外面的对象接受此返回值,需要先new int[10000000],然后memcpy,然后delete [] num; 在有了移动语义后,这种情况只需num = num;就行了,即函数外面的对象保存下返回值对象内的int*指针成员就行了。 从性能上来说,如果需要新分配和拷贝的内存较小的话,差别太小,微乎其微,感觉不出来。 但如果需要新分配和拷贝的内存较大,像上面我举的这个例子的情况new int[10000000]这种在堆上分配大内存的动作,是相当相当耗时的,甚至运行后人都能明显感觉出来。意义就比较大了
回复 点赞
vanxining 2014年01月31日
引用 2 楼 mougaidong 的回复:
这不是move constructor的典型应用场景。在NRVO和RVO的情况下,copy constructor和move constructor 都可以被优化。也就是说move constructor在这种情况下(很可能)起不到什么作用,但是不代表它就没有作用。 照在我使用C++11的过程中个人体会来看,可以说,move语义的出现是使得C++从03版到11新版的核心推进力量。而move constructor 又是move语义得以贯彻施行的一个主要途径。 说了这么多,那么move constructor到底是为什么而生的,这其实还是要从move语义说起。move语义的本质之处,归纳起来也就一个字:“偷”。将一个右值对象中的堆内存通过指针的方式转移到另一个对象,则是偷的典型应用场景。 http://en.cppreference.com/w/cpp/language/move_constructor
这位朋友说了一大堆,似乎都没说到点上。 1、move语义只是C++ 11标准中提升运行时性能最重要的一个手段。然而究竟C++ 11中哪个新特性才是其“核心推进力量”,恐怕B.S.也无法说出个所以然来; 2、RVO(Return value optimization) == NRVO(Named return value optimization); 3、我无法看出楼主给出的例子和链接中的例子在原理上有什么差别。
回复 点赞
李刚弄死他 2014年01月31日
引用 8 楼 mujiok2003 的回复:
1. RVO和NRVO不是强制要求 2. 有时候RVO和NRVO不适用

std::string a, b, c;
//....
std::string combine = a + "_" + b + "_" + c; //这里会好几个临时对象
3. 有些对象不时能拷贝的,但是可以move的 比如std::fstream, std::thread 4. move不是在所有场景下都立竿见影.
大家再讨论一下吧。
回复 点赞
李刚弄死他 2014年01月30日
引用 2 楼 mougaidong 的回复:
这不是move constructor的典型应用场景。在NRVO和RVO的情况下,copy constructor和move constructor 都可以被优化。也就是说move constructor在这种情况下(很可能)起不到什么作用,但是不代表它就没有作用。 照在我使用C++11的过程中个人体会来看,可以说,move语义的出现是使得C++从03版到11新版的核心推进力量。而move constructor 又是move语义得以贯彻施行的一个主要途径。 说了这么多,那么move constructor到底是为什么而生的,这其实还是要从move语义说起。move语义的本质之处,归纳起来也就一个字:“偷”。将一个右值对象中的堆内存通过指针的方式转移到另一个对象,则是偷的典型应用场景。 http://en.cppreference.com/w/cpp/language/move_constructor
也就是说,实际作用不大喽。
回复 点赞
孩皮妞野 2014年01月30日
并不能保证所有时候都有性能上的提升,像你举例的情形,没有太大的定义一个移动构造函数的必要,性能上的提升维护其微。但是如果把你的例子稍稍变一下,相像你存的不是一个char, 而是一个array of char呢?移动时的代价不变,而赋值构造函数则必须把char 数组的内容复制一遍。如果这个char数组够大,比如1GB, 会节省多少时间? 在C++11之前,如果函数要返回一个std::string, std::vector<std::vector<std::vector<std::string> > >, 你没有办法不复制一份新(甚至2次或多次复制)的,再把旧的扔掉, std::vector<std::vector<std::vector<std::string> > >析构也还需要时间,那是后C++程序员能做的是不得不把返回值作为引用参数传递来避免这笔不必要的开销。 instead of

std::vector<std::vector<std::vector<std::string> > > some_function();
, use

std::vector<std::vector<std::vector<std::string> > >& some_function(std::vector<std::vector<std::vector<std::string> > >& modify_this_and_return_it);
这个常常意味着表达能力上的损失---代码读起来没有那么美。
回复 点赞
menzi11 2014年01月30日
你把代码都写出来了还需要问吗。。。活活多了一个new啊!
回复 点赞
珍惜生命远离CPP 2014年01月29日
就你写的例子,函数参数为test&&时和const test& 、test相比,可以明显减少new char的次数。
回复 点赞
发动态
发帖子
C++ 语言
创建于2007-09-28

3.1w+

社区成员

24.8w+

社区内容

C++ 语言相关问题讨论,技术干货分享
社区公告
暂无公告