当前位置:首页 » 编程博文
开发技术指南» 文章正文
    引言: 泛型<编程>:转移构造函数 ainText&quo
 

 

 ·getting started with the swt    »显示摘要«
    摘要:去www.swt-designer.com下载swt designer的时候发现了这个页面,赶紧做个标记。 1. installing eclipse and running a simple swt program tutorial (or in postscript) and workspace zip file. 2. basic widgets tutorial (or in postsc......
 ·有关windows服务的创建,控制    »显示摘要«
    摘要:1,创建, 打开vs.net , 新建一个项目,类型为windows service2,安装, installutil service1.exe installutil /u service1.exe 删除3,控制 servicecontroller sc=new servicecontroller(servername); sc.start(); sc.stop();sc.continue();......


泛型:转移构造函数
泛型<编程>:转移构造函数

    我想你们知道得很清楚,创建,拷贝,并摧毁临时对象是你的c++编译器爱做的事,临时对象在内部被创建,不幸的是这极大影响到了c++程序的执行效率.实际上,临时对象是c++程序中最大的影响效率因素. 【程序编程相关:介绍 IOC

andrei alexandrescu 【推荐阅读:一次代码重构记录

  【扩展信息:应用Java技术实现数据库应用系统

    这样的代码看上去不错:

vector<string> readfile();

vector<string> vec = readfile();

 

或:

 

string s1, s2, s3;

s1 = s2 + s3;

 

    如果你需要效率,你就不要用这样的代码.readfile()与operator+创造的临时对象分别被拷到目标对象,然后被丢弃——多么浪费!

    为了解决这个问题,你需要遵循不那么美观的规范.例如,你应该把目标对象作为函数的一个参数传进去.

 

void readfile(vector<string>& dest);

vector<string> dest;

readfile(dest);

   

    这真麻烦.更糟的是,操作符函数不给你这个选择,所以如果你要高效处理大对象,你必须限制自己不用会建立临时对象的操作符:

 

string s1, s2, s3;

s1 = s2;

s1 += s3;

 

    这些笨拙的常用法经常在大项目组的大程序中蔓延,不断带来麻烦,影响了写代码的乐趣,并增加了代码行数.如果我们能从函数中返回值,使用操作符,并自由传递临时对象,却不必担心会有大量的创建/拷贝/摧毁动作带来的时间上的浪费,这该多好.

    这可能吗?

    实际上这已经不是“这该多好”这样的梦想了.整个c++社区都要求解决这个多余的拷贝问题.对这个问题存在着广泛的兴趣.已经有了一个正式的草案,已经被提交到标准委员会了[2].该草案采用基于语言的解决方案.这方面的讨论在usenet中到处都是,你现在在读的这篇文章已经被热烈地讨论过了.

    本文告诉你怎样解决这个存在于c++中的不必要的拷贝问题.没有100%合适的解决方法,但可以达到很大程度上的改善.我们会一步一步地建立一个完整的强大的框架,来帮你消除你程序中多余的拷贝临时对象.这个解决方法不是100%透明的,但它的确去掉了所有不必要拷贝,并提供足够的封装来作为一个可靠的替代品,直到几年后,一个更干净的,基于语言的方法被定为标准并被实现.

 

临时对象与“转移构造函数(move constructor)”

    在与临时对象搏斗了一阵后,人们意识到去除真正的临时对象在大多数情况下不是真正的问题所在.很多时候,问题在于消除不必要的临时对象拷贝.请让我来详细解释一下.

    多数“昂贵拷贝”的数据结构以指针或句柄形式存储它们的数据.典型的例子是:一个string类型存放一个大小与一个char*,一个matrix类型存放一些维度的整数与一个double*,或一个file类型存放一个句柄.

    正如你看到的,拷贝string,matrix或file的花销并不来于拷贝实际的数据成员,而来自于复制被指针或句柄所引用的数据.

    知道这些后我们的目标是消除拷贝,一个好方法是检查一个临时对象.反正那个对象要被摧毁,我们可以乘它还可以利用的时候利用它.

    但怎样才算是一个临时对象?我们来提出一个有争议的定义:

在一个环境中,当且仅当直到一个对象退出环境时只有析构函数是唯一操作它的函数,这个对象被认为是临时的

    这个环境可以是一个表达式或一个域(比如函数体)

    c++标准没有定义临时对象,但它认为临时对象是匿名的(比如函数返回值).用我们的(更一般化)的定义,函数中定义的命名的栈变量也是临时对象,我们稍后会利用到这个想法带来的好处.

    考虑这个普通的string类实现

 

class string

{

    char *data_;

    size_t length_;

public:

    ~string()

    {

        delete[] data_;

    }

    string(const string& rhs)

        : data_(new char[rhs.length_]), length_(rhs.length_)

    {

        std::copy(rhs.data_, rhs.data_ + length_, data_);

    }

    string& operator=(const string&);

    ….

};

    在这里拷贝的开销主要在于复制data_,就是分配新的内存并拷贝它,如果我们可以知到rhs实际上是个临时对象那该多好.考虑下面c++伪代码:

 

class string

{

    ...同上...

    string(temporary string& rhs)

        : data_(rhs.data_), length_(rhs.length_)

    {

        //复位源字符串,这样它可以被摧毁

        //别忘了析构函数仍然对临时对象执行

        rhs.data_ = 0;

    }

    ……

};

   

    当你从一个我们所定义的临时对象(比如一个函数调用的返回值)构造一个string时这个假想的重载构造函数string(temporary string&)起作用.然后,通过简单地拷贝指针(用不着复制它所指向的内存块),构造函数把rhs转移到被创建的对象.最后但非常重要的,转移构造函数复位源指针rhs.data_.这样,当临时对象被摧毁时,delete[]会无害地作用于一个空指针上.

    一个重要的细节是转移构造完后rhs.length_不设为零.这从理论上说是不正确的(我们得到一个无效的string,它的data == 0,length_!= 0),但这样做有一个好的理由.rhs的最终状态不必是一致的.这是因为对rhs要执行的唯一操作是析构函数——没有其他的了.所以只要rhs满足能被摧毁的条件,它根本没必要被认为是一个合法的字符串.

    转移构造函数是个消除不必要的拷贝临时对象的好方法.我们只有一个小问题——c++语言中没有temporary关键字.

    (应该指出的是检查临时对象不是对所有类都有用.有时候,所有数据都直接存放到容器里,比如:

 

class fixedmatrix

{

    double data_[256][256];

public:

...操作函数...

};

 

    对这样一个类,真的拷贝所有sizeof(fixedmatrix)个字节是个昂贵的操作,而且检查临时对象没什么用处)

 


...   下一页
    摘要:--------------------------------------------------------------------------------------------------------------------- 这篇文章写于三年前,当时手中拿着stevens的三本巨著 tcp v1/v2/v3 的摸不着头脑-因为它们太厚了。我知道我不需要那么多,那么详细。于是有了自己分析源......
» 本期热门文章:

©2000-2007 All Rights Reserved. 最佳浏览:1024X768 MSIE