Source of c++ temporary objects

  • 2020-04-01 21:28:27
  • OfStack

First look at the code at the bottom:


 #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;
 }

  The results for
  -- -- -- -- -- 1 & 2 have spent  
  Most gardeners may think that "int temp" is a "temporary object", but in fact, "int temp" is just a local variable of the swap function.

Temporary objects are objects that are not visible in the code, but do exist in the actual program. Temporary objects are compiler aware.


Why study temporary subjects?
The main purpose is to improve the performance and efficiency of the program, because the construction and destruction of temporary objects is not small to the system overhead, so we should understand them, know how they are caused, so as to avoid them as much as possible.

Temporary objects create temporary objects by creating an unnamed non-heap object. (if you don't know what a heap object is and what a non-heap object is, you can refer to C++ in this blog post about what you should not do.) This unnamed object is typically generated under three conditions: when an implicit cast is performed for a function to be successfully called, when a function parameter is passed, and when a function returns an object.

So let's first look at implicit casting in order for the function to be called successfully.


 #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;
 }

The result is:

The < img border = 0 style = "LINE - HEIGHT: 1.5" Alt = "" SRC =" / / files.jb51.net/file_images/article/201301/201301021748222.jpg ">

Here we call the function countChar(const STD ::string& s,const char& c), so let's see that the parameter of this function is const STD ::string& s, the parameter is const STD ::string, but what we're actually passing is the char buffer[200] array. In fact, in order to make the function call successful, the compiler casts char * to STD ::string, which is done by an assignment constructor that takes the buffer as an argument to build a temporary object of type STD ::string. When constChar returns, the function is undone, and the STD ::string temporary object is released. But in fact from the whole program, the temporary object construction and release is unnecessary overhead, we can improve the efficiency of the code to modify the code to avoid indifferent conversion. So knowing where the temporary objects are coming from can give your program a small performance boost.

    Note that this type of conversion occurs only when an object is passed by value or when a constant reference parameter is passed, not when a parameter object is passed by an unusual reference. Because the intention of passing an infrequent reference parameter is to change the value of the passed parameter through a function, the function is actually a temporary object created by the changed cast, so the intent cannot be realized and the compiler simply rejects it.

The second case is when a familiar function passes an argument and constructs the corresponding temporary object. Look at the results of the following piece of code.


 #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;
}

The result is:

< img Alt = "" border = 0 SRC =" / / files.jb51.net/file_images/article/201301/201301021748223.jpg ">

Here we analyze that the first two "h2" are printed by calling the constructor People(STD ::string n,int a), while "h3" is printed by calling the copy constructor People(const People&) to create temporary objects, and h1 is printed by calling the default constructor People(). So how do you avoid creating temporary objects? Quite simply, we do this by referencing arguments

Void swap (People & p1, People & p2)
The third scenario is when a function returns an object. Note here that the temporary object is created by the copy constructor.

For example,     Const Rationanl operator+(Rationanl a,Rationanl b) the return value of the function is temporary, because it is not named, it is just the return value of the function. Each time you have to pay a price for calling add to construct and release this object.


 #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 is optimized and no temporary variables are seen. The compiler simply skips creating r3, using an assignment notation
     r3.show( );
     //Equivalent to   (r1, r2 *.) the show ();
     return 0;
 } 

The result is:

< img Alt = "" border = 0 SRC =" / / files.jb51.net/file_images/article/201301/201301021748224.jpg ">

Here is a pity that we did not see the results we expected to see, the results should be h2,h2,h2,h3,h4, should be in the return value when there is an assignment constructor, the establishment of temporary variables, later by the author of online search data to confirm that GCC has been optimized.


Related articles: