+ -
当前位置:首页 → 问答吧 → 大家帮忙算一算拷贝构造函数的调用次数

大家帮忙算一算拷贝构造函数的调用次数

时间:2011-12-17

来源:互联网

C/C++ code

#include<iostream>
using namespace std;
class Myclass
{
    public:

       Myclass(int n) { number=n;}
       Myclass(const Myclass &oher) { number=other.number;}
       ~Myclass();
    private:
        int number;
};

Myclass fun(Myclass p) //1次
{
     Myclass temp(p);//1次
     return temp;//1次,这里我认为系统会建立一个无名对象以存放temp的值,所以有一次赋值。
}
int main()
{
   Myclass obj1(10),obj2(0);
   Myclass obj3(obj1);          //1次
   obj2=fun(obj3); // fun(obj3)是3次,赋值又1次,所以这里是4次
   return 0;
}

//综上共五次



想听听大家的看法!!!

作者: neolyao   发布时间: 2011-12-17

具名返回值优化可能会省掉一次

Google “NRVO”

作者: mougaidong   发布时间: 2011-12-17

如果你用的是VC系列带的编译器的话,会有此优化

作者: mougaidong   发布时间: 2011-12-17

赋值就是赋值,哪来的又一次,赋值调用的是赋值函数

作者: mougaidong   发布时间: 2011-12-17

引用 3 楼 mougaidong 的回复:
赋值就是赋值,哪来的又一次,赋值调用的是赋值函数

赋值难道不调用构造函数吗??

作者: neolyao   发布时间: 2011-12-17

C/C++ code
#include<iostream>
using namespace std;
class Myclass
{
public:
    
    Myclass(int n) 
    { 
        number=n;cout<<"Initialize !"<<endl;
    }
    Myclass(const Myclass &other)
    {
        number=other.number;
        cout<<"copy obj !"<<endl;
    }
    ~Myclass() 
    {
        cout<<"release !"<<endl;
    }
private:
    int number;
};

Myclass fun(Myclass p) //1次
{
    Myclass temp(p);//1次
    return temp;//1次,这里我认为系统会建立一个无名对象以存放temp的值,所以有一次赋值。
}

int main()
{
    Myclass obj1(10),obj2(0);
    Myclass obj3(obj1);          //1次
    obj2=fun(obj3); // fun(obj3)是3次,赋值又0次,所以这里是3次
    return 0;
}

//综上共4次

作者: agoago_2009   发布时间: 2011-12-17

引用 4 楼 neolyao 的回复:

引用 3 楼 mougaidong 的回复:
赋值就是赋值,哪来的又一次,赋值调用的是赋值函数

赋值难道不调用构造函数吗??

要啊

作者: agoago_2009   发布时间: 2011-12-17

Myclass fun(Myclass p) 
{
  Myclass temp(p);//1次
  return temp;///1次
}

int main()
{
  Myclass obj1(10),obj2(0);
  Myclass obj3(obj1); //1次
  obj2=fun(obj3); // fun(obj3)是2次,赋值又1次,所以这里是3次
  return 0;
}

作者: agoago_2009   发布时间: 2011-12-17

在拷贝构造函数里面写个输出语句测试下~~
这里涉及到一个临时变量优化的问题,不同编译器下可能不一样

作者: qscool1987   发布时间: 2011-12-17

这里我找到一篇文章写的比较好:
拷贝构造函数和赋值函数到底有什么差别呢?我想有人会说,没有差别。呵,如果没有差别,那么只要实现其中一个就行了,何必要两者都实现呢?不绕圈子了,它们的差别是:
拷贝构造函数对同一个对象来说只会调用一次,而且是在对象构造时调用。此时对象本身还没有构造(无空间),无需要去释放自己的一些资源。而赋值操作可能会调用多次,你在复制之前要释放自己的一些资源,否则会造成资源泄露。

 
对上面的内容作适当的说明:假设有类
C/C++ code
 

class A{

public:

    A(const int &a); //a

    A(const A&);//b

    A& operator=(const A&);//c

   ……

Private:

Int a;

}


//a行为一般构造函数,b行为拷贝构造函数,c行为赋值操作(即operator=重载)

//(一)    默认成员函数

//当程序员没用自己定义的情况获一个空类,编译器会自动的为该类生成6个成员函数,分别为:

//1个构造函数

//1个析构函数

//1个拷贝构造函数

//1个赋值操作函数operator=

//2个取地址函数operator&,其中一个为const版本一个为非const版本



 

程序猿最好能够重新定义这些函数,以避免这些函数的自动调用产生不能预料的结果。这些自动生成的函数的实现部分:(参考effective C++条款45)

C/C++ code
inline A::A() {}

inline A::~A() {}

inline A *  A::operator&() { return this; }

inline const A * A::operator&() const
{ return this; }

 


(二) 拷贝构造函数

C/C++ code

A   a1 = new A();

A   a2(a1);//a

A   a3;//3

a3 = a1;//4


此处a2的构造使用的拷贝构造,这点基本上都能明白,也容易理解。等4行的a3调用赋值操作。

 

但有的时候 同样是=,确调用了拷贝构造,那到底

什么时候调用拷贝构造?

什么时候调用赋值操作?

A a1;

A a2 = a1; //b

上面中a2初始化,此处调用拷贝构造。

大家可能看出来了行a、b的差别。那行4与行b的差别呢:行4中调用赋值操作,是应为a3已经初始化过(行3),此时a3以调用A()进行了内存空间开辟。此时=只需要将a1中的内容复制到a3的存储空间中。所以使用的是复制操作(operator=);

行b的情况则是这样,由于a2刚定义,尚未有内存空间,并且需要用a1去初始化它,所以需要两部操作,1.为a2分配空间;2.a1内容复制到a2空间。这一行操作相当于行3+行4的效果。

 

另外拷贝构造还在如下地方隐含调用:

1. 函数参数传对象的值时

Bool Fun(A a);

A b;

Fun(b);//对象值传递

此时在开始Fun函数时会创建一个临时对象t_b,类似:

Bool Fun( A b){

A t_b(b); //a

……

}

A行后的内容针对t_b进行。

 

2. 函数返回临时对象时

A Fun(){

A temp;

Return temp;//b

}

此处b行也会隐含调用拷贝构造产生一个临时对象作为返回,而真正的temp在}后将背销毁。

 

这里说道隐含调用,提一下:

修饰符explicit:如果不希望构造被隐含转换,则可将其定义为explicit,则禁止“隐式“转换。某些情况下”隐式“将会产生错误。Explicit主要目的是指明一个函数的调用必须是显式的,副作用才是禁止隐式的类型转化。如:

函数 :

A& test(A&);

Test(2);//成功,做了隐式转换,将int 型2转换为A类型。

如果A类定义中的构造函数A(const int &a)加上explicit,则:

Test(2);//失败,参数类型不匹配错误。

 

同样的

A a=3;//失败 类似的情况

A a(3);//成功

作者: neolyao   发布时间: 2011-12-17

引用 9 楼 neolyao 的回复:

这里我找到一篇文章写的比较好:
拷贝构造函数和赋值函数到底有什么差别呢?我想有人会说,没有差别。呵,如果没有差别,那么只要实现其中一个就行了,何必要两者都实现呢?不绕圈子了,它们的差别是:
拷贝构造函数对同一个对象来说只会调用一次,而且是在对象构造时调用。此时对象本身还没有构造(无空间),无需要去释放自己的一些资源。而赋值操作可能会调用多次,你在复制之前要释放自己的一些资源,否则会造成资源泄……

学习了

作者: agoago_2009   发布时间: 2011-12-17

定义个简单的宏跟踪代码

取决于赋值操作符怎么定义

用缺省的赋值操作符,你这里共拷贝赋值4次

C/C++ code
#define output(str) cout <<endl <<"\t" <<#str <<endl; str

class Myclass
{
public:
    Myclass(int n):number(n){}
    Myclass(const Myclass &other):number(other.number){cout <<"copy constructor\t" <<number <<endl;}
    //Myclass& operator=(const Myclass &){cout <<"assignment operator\t" <<number <<endl;return *this;}
    Myclass operator=(const Myclass &){cout <<"assignment operator\t" <<number <<endl;return *this;}
    ~Myclass(){};
private:
    int number;
};

Myclass fun(Myclass p) //1次
{
    output(Myclass temp(p);)//1次
    output(return temp;)//1次
}

int main()
{
    output(Myclass obj1(10);)
    output(Myclass obj2(0);)

    output(Myclass obj3(obj1);)          //1次
    
    output(obj2=fun(obj3);) // fun(obj3)是3次,赋值又1次,所以这里是4次
    output(return 0;)
}

作者: yisikaipu   发布时间: 2011-12-17

拷贝构造4次

C/C++ code

        Myclass obj1(10);

        Myclass obj2(0);

        Myclass obj3(obj1);
copy constructor        10

        obj2=fun(obj3);
copy constructor        10

        Myclass temp(p);
copy constructor        10

        return temp;
copy constructor        10

        return 0;
请按任意键继续. . .

作者: yisikaipu   发布时间: 2011-12-17

更正#11楼
C/C++ code
#define output(str) cout <<endl <<"\t" <<#str <<endl; str

class Myclass
{
public:
    Myclass(int n):number(n){}
    Myclass(const Myclass &other):number(other.number){cout <<"copy constructor\t" <<number <<endl;}
    //Myclass& operator=(const Myclass &){cout <<"assignment operator\t" <<number <<endl;return *this;}
    //Myclass operator=(const Myclass &){cout <<"assignment operator\t" <<number <<endl;return *this;}
    ~Myclass(){};
private:
    int number;
};

Myclass fun(Myclass p)//1次
{
    output(Myclass temp(p);)//1次
    output(return temp;)//1次
}

int main()
{
    output(Myclass obj1(10);)
    output(Myclass obj2(0);)

    output(Myclass obj3(obj1);)//1次
    
    output(obj2=fun(obj3);)
    output(return 0;)
}

作者: yisikaipu   发布时间: 2011-12-17

引用 7 楼 agoago_2009 的回复:

Myclass fun(Myclass p)
{
Myclass temp(p);//1次
return temp;///1次
}

int main()
{
Myclass obj1(10),obj2(0);
Myclass obj3(obj1); //1次
obj2=fun(obj3); // fun(obj3)……

发错了的

作者: agoago_2009   发布时间: 2011-12-17

你们觉得该结贴没有?

作者: neolyao   发布时间: 2011-12-17

已经亲测了,VS2008下:
1、debug版本,4次。
2、release版本,3次。

作者: mougaidong   发布时间: 2011-12-17

C/C++ code
#include<iostream>
using namespace std;
class Myclass
{
public:

    Myclass(int n) { number=n;}
    Myclass(const Myclass &oher) { cout << "copy" << endl; }
    ~Myclass(){};
private:
    int number;
};

Myclass fun(Myclass p) //1次
{
    Myclass temp(p);//1次
    return temp;//1次,这里我认为系统会建立一个无名对象以存放temp的值,所以有一次赋值。
}

int main()
{
    Myclass obj1(10), obj2(0);
    Myclass obj3(obj1);          //1次
    obj2=fun(obj3); // fun(obj3)是3次,赋值又1次,所以这里是4次
    return 0;
}

作者: mougaidong   发布时间: 2011-12-17

引用 16 楼 mougaidong 的回复:
已经亲测了,VS2008下:
1、debug版本,4次。
2、release版本,3次。

2、release版本,3次。
是不是“具名返回值优化可能会省掉一次”

作者: neolyao   发布时间: 2011-12-17

C/C++ code

obj2=fun(obj3); // fun(obj3)是3次,赋值不会调用构造函数了,因为obj2已经存在,如何构造。
                  //所以这里是3次

作者: neolyao   发布时间: 2011-12-17

引用 18 楼 neolyao 的回复:

引用 16 楼 mougaidong 的回复:
已经亲测了,VS2008下:
1、debug版本,4次。
2、release版本,3次。

2、release版本,3次。
是不是“具名返回值优化可能会省掉一次”


是的

作者: mougaidong   发布时间: 2011-12-17

引用 19 楼 neolyao 的回复:

C/C++ code

obj2=fun(obj3); // fun(obj3)是3次,赋值不会调用构造函数了,因为obj2已经存在,如何构造。
//所以这里是3次


http://blog.csdn.net/mougaidong/article/details/6927216

作者: mougaidong   发布时间: 2011-12-17

引用 9 楼 neolyao 的回复:
这里我找到一篇文章写的比较好:
拷贝构造函数和赋值函数到底有什么差别呢?我想有人会说,没有差别。呵,如果没有差别,那么只要实现其中一个就行了,何必要两者都实现呢?不绕圈子了,它们的差别是:
拷贝构造函数对同一个对象来说只会调用一次,而且是在对象构造时调用。此时对象本身还没有构造(无空间),无需要去释放自己的一些资源。而赋值操作可能会调用多次,你在复制之前要释放自己的一些资源,否则会造成资源泄露……

这些说明的是拷贝构造和赋值的区别,并不能说明你这个问题,你这个问题我还是我上面说的编译器优化的问题,对于临时变量的拷贝构造不同的编译器优化效果还很有区别
Return temp;
这里肯定是先产生一个临时对象,这个临时对象如何产生就很讲究了
测试发现,vc下是创建一个临时对象将temp拷贝到临时对象中
GCC下目前还没看懂,优化的更厉害

作者: qscool1987   发布时间: 2011-12-17

先退出去下,我上汇编和栈结构给你好好整整这个问题

作者: qscool1987   发布时间: 2011-12-17