c++ 临时对象的来源

所属分类: 软件编程 / C 语言 阅读数: 95
收藏 0 赞 0 分享

首先看下面一端代码:

复制代码 代码如下:

 #include <iostream>
 void swap( int &a,int &b)
 {
     int temp;
     temp=a;
     a=b;
     b=temp;
 }

 int main(int argc,char** argv)
 {
     int a=1,b=2;
     swap(a,b);
     std::cout<<a<<"-----"<<b<<std::endl;
     return 0;
 }

 结果为
 2-----1  
 可能大多数园友,认为"int temp"是"临时对象",但是其实不然,"int temp"仅仅是swap函数的局部变量。

临时对象是代码中看不到的,但是实际程序中确实存在的对象。临时对象是可以被编译器感知的。


为什么研究临时对象?
主要是为了提高程序的性能以及效率,因为临时对象的构造与析构对系统开销也是不小的,所以我们应该去了解它们,知道它们如何造成,从而尽可能去避免它们。

临时对象建立一个没有命名的非堆对象会产生临时对象。(不了解什么是堆对象和非堆对象,可以参考C++你最好不要做的这一博文,这里面有介绍。)这种未命名的对象通常在三种条件下产生:为了使函数成功调用而进行隐式类型转换时候、传递函数参数和函数返回对象时候。

那么首先看看为了使函数成功调用而进行隐式类型转换。

复制代码 代码如下:

 #include <iostream>
  int countChar(const std::string & s,const char c)
  {
      int count=0;
      for(int i=0;i<s.length( );i++)
      {
          if(*(s.c_str( )+i) == c)
          {
              count++;
         }
    }
     return count;
 }

 int main(int argc,char** argv)
 {
     char buffer[200];
     char c;
     std::cout<<"please input the string:";
     std::cin>>buffer;
     std::cout<<"please input the char which you want to chount:";
     std::cin>>c;
     int count=countChar(buffer,c);
     std::count<<"the count is:"<<count<<std::endl;
     return 0;
 }

结果为:



这里调用函数countChar(const std::string& s,const char& c),那么我们看看这个函数的形参是const std::string &s,形参类型为const std::string,但是实际上传递的是char buffer[200]这个数组。其实这里编译器为了使函数调用成功做了类型转换,char *类型转换为了std::string类型,这个转换是通过一个赋值构造函数进行的,以buffer做为参数构建一个std::string类型的临时对象。当constChar返回时,即函数撤销,那么这个std::string临时对象也就释放了。但是其实从整个程序上来说临时对象的构造与释放是不必要的开销,我们可以提高代码的效率修改一下代码避免无所谓的转换。所以知道临时对象的来源,可以对程序性能上有一个小小提升。

   注意仅当通过传值方式传递对象或者传递常量引用参数,才会发生这类型的转换,当传递非常量引用的参数对象就不会发生。因为传递非常量的引用参数的意图就是想通过函数来改变其传递参数的值,但是函数其实是改变的类型转换建立的临时对象,所以意图无法实现,编译器干脆就直接拒绝。

第二种情况是大家熟悉的函数传递参数的时候,会构造对应的临时对象。看下面一段代码运行的结果想必就一清二楚了。

复制代码 代码如下:

 #include<iostream>
 class People
 {
     public:
         People(std::string n,int a)
         :name(n),age(a)
         {
             std::count<<"h2"<<std::endl;
         }
        People( )
        {
            std::count<<"h1"<<std::endl;
        }
        People(const People& P)
        {
            name=p.name;
            age=p.age;
            std::cout<<"h3"<<std::endl;
        }
        std::string name;
        int age;
};

void swap(People p1,People p2)
{
    People temp;
    temp.age=p1.age;
    temp.name=p1.name;
    p1.age=p2.age;
    p1.name=p2.name;
    p2.age=temp.age;
    p2.name=temp.name;
}

int main(int argc, char ** argv)
{
    People p1("tom",18),p2("sam",19);
    swap(p1,p2);
    return 0;
}


结果为:



这里分析下前面两个"h2"是通过调用构造函数People(std::string n,int a)打印出来的,而"h3"就是通过调用复制构造函数People(const People&)而建立临时对象打印出来的,h1是调用默认构造函数People( )打印出来的。那么怎么避免临时对象的建立呢?很简单,我们通过引用实参而达到目的

void swap(People &p1,People &p2)
第三种情景就是函数返回对象时候。这里要注意临时对象的创建是通过复制构造函数构造出来的。

例如   const Rationanl operator+(Rationanl a,Rationanl b)该函数的返回值的临时的,因为它没有被命名,它只是函数的返回值。每回必须为调用add构造和释放这个对象而付出代价。

复制代码 代码如下:

 #include <iostream>
 class Rationanl
 {
     public:
         Rationanl(int e,int d)
         :_elemem(e),_denom(d)
         {
             std::cout<<"h2"<<std::endl;
         }
         void show( ) const;
         int elemem() const {return _elemem;}
         int denom() const {return _denom;}
         void  setElemon(int e){_elemon=e;}
         void  setDenom(int d) {_denom=d;}
         Rationanl(const Rationanl &r);
         Rationanl & operator=(const Rationanl &r);
     private:
         int _elemem;
         int _denom;
 };
 Rationanl::Rationanl(const Rationanl &r)
 {
     setElemon(r.elemon( ));
     setDenom(r.denom( ) );
     std::cout<<"h3"<<std::endl;
 }
 Rationanl & Rationanl::operator=(const Rationanl &r)
 {
     setElemon(r.elemon( ));
     setDenom(r.denom( ) );
     std::cout<<"h4"<<std::endl;
     return *this;
 }

 void Rationanl::show( )
 {
         std::cout<<_elemen<<"/"<<_denom<<std::endl;
 }
 const Rationanl operator*(const Rationanl lhs,const Rationanl rhs)
 {
     return Rational result(lhs.elemen*rhs.elemen,rhs.denom*rhs.denom);
 }

 int main(int argc,char **argv)
 {
     Rationanl r1(1,2),r2(1,3)
     Rationanl r3=r1*r2;    //GCC做了优化,没有看到临时变量。编译器直接跳过建立r3,使用赋值符号
     r3.show( );
     //相当于  (r1*r2).show( );
     return 0;
 }

结果为:



这里很可惜没有看到我们想到看到的结果,结果应该为h2,h2,h2,h3,h4,应该是在返回值的时候有一个赋值构造函数,建立临时变量的,后来经笔者网上查找资料证实GCC做了优化。

更多精彩内容其他人还在看

利用C语言来求最大连续子序列乘积的方法

这篇文章主要介绍了利用C语言来求最大连续子序列乘积的方法,基本的思路以外文中还附有相关ACM题目,需要的朋友可以参考下
收藏 0 赞 0 分享

用C语言判断一个二叉树是否为另一个的子结构

这篇文章主要介绍了用C语言判断一个二叉树是否为另一个的子结构,是数据结构学习当中的基础知识,需要的朋友可以参考下
收藏 0 赞 0 分享

C语言实现的阶乘,排列和组合实例

这篇文章主要介绍了C语言实现的阶乘,排列和组合的方法,涉及C语言数学运算的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

C语言查找数组里数字重复次数的方法

这篇文章主要介绍了C语言查找数组里数字重复次数的方法,涉及C语言针对数组的遍历与判断技巧,具有一定参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

C语言简单实现计算字符个数的方法

这篇文章主要介绍了C语言简单实现计算字符个数的方法,涉及C语言针对字符串的简单遍历与判定技巧,具有一定参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

c实现linux下的数据库备份

本文给大家简单介绍下c实现linux下的数据库备份的方法和具体的源码,十分的实用,有需要的小伙伴可以参考下。
收藏 0 赞 0 分享

C++获得文件状态信息的方法

这篇文章主要介绍了C++获得文件状态信息的方法,包括文件状态信息、文件所在磁盘盘符、文件创建时间、访问时间及修改日期等,需要的朋友可以参考下
收藏 0 赞 0 分享

C语言按关键字搜索文件夹中文件的方法

这篇文章主要介绍了C语言按关键字搜索文件夹中文件的方法,涉及C语言文件操作及字符串查找的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

C语言之字符串模糊查询方法的实现

本篇文章主要为大家介绍字符串模糊查询的C语言程序编写方法,有需要的朋友可以参考下
收藏 0 赞 0 分享

C语言实现BMP转换JPG的方法

这篇文章主要介绍了C语言实现BMP转换JPG的方法,涉及C#图片格式转换的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享
查看更多