鱼C论坛

 找回密码
 立即注册
查看: 2233|回复: 3

求高手解答,关于c++为什么不调用复制构造函数!

[复制链接]
发表于 2014-4-17 10:51:35 | 显示全部楼层 |阅读模式
10鱼币
#include<iostream>
using namespace std;
class A
{
public:
        A(){i=1;cout<<"构造函数执行中!"<<endl;}
        ~A(){cout<<"析构函数执行中!"<<endl;}
        A(A&a){i=a.i;cout<<"复制构造函数执行中!"<<endl;}
       A(int x){i=x;cout<<"带参数的函数执行中!"<<endl;}
    void set(int x){i=x;}
        int get(){return i;}
        A operator++()
        {
                i++;
               
                return A(i);
        }
private:
        int i;
};
int main()
{
A a;
cout<<"a:"<<a.get()<<endl;
A b=++a;
cout<<"b:"<<b.get()<<endl;
return 0;
}

最佳答案

查看完整内容

复制构造函数:单个对本类类型引用形参(常用const修饰)。当定义一个新对象并用同一个类型的对象对它进行初始化式,将显式使用复制构造函数,当该类型的对象传递给函数或从函数返回该类类型的对象时,将隐式调用复制构造函数。复制构造函数的形参可以不为const,但必须是一个引用。否则每当以pass-by-value传递参数时,调用一个复制构造函数都会导致无穷递归。如果不定义复制构造函数,编译器将合成一个,因此若要完全禁止复制(i ...
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2014-4-17 10:51:36 | 显示全部楼层

复制构造函数:单个对本类类型引用形参(常用const修饰)。当定义一个新对象并用同一个类型的对象对它进行初始化式,将显式使用复制构造函数,当该类型的对象传递给函数或从函数返回该类类型的对象时,将隐式调用复制构造函数。

复制构造函数的形参可以不为const,但必须是一个引用。否则每当以pass-by-value传递参数时,调用一个复制构造函数都会导致无穷递归。

如果不定义复制构造函数,编译器将合成一个,因此若要完全禁止复制(iostream类就不允许复制),禁止用户复制该类类型而且连友元和成员函数都不能进行复制:在private中声明但不定义一个复制构造函数。用户代码中的复制尝试将在编译时标记为错误(private),而成员函数和友元中的复制尝试将在连接时导致错误

有些类必须对复制对象时发生的事情加以控制,这样的类经常有一个数据成员是指针或者有成员表示在构造函数中分配的其他资源     把参数传递给函数有三种方法,一种是值传递,一种是传地址,还有一种是传引用。前者与后两者不同的地方在于:当使用值传递的时候,会在函数里面生成传递参数的一个副本,这个副本的内容是按位从原始参数那里复制过来的,两者的内容是相同的。当原始参数是一个类的对象时,它也会产生一个对象的副本,不过在这里要注意。一般对象产生时都会触发构造函数的执行,但是在产生对象的副本时却不会这样,这时执行的是对象的复制构造函数。为什么会这样?嗯,一般的构造函数都是会完成一些数据成员初始化的工作,在对象传递给某一函数之前,对象的一些属性可能已经被改变了,如果在产生对象副本的时候再执行对象的构造函数,那么这个对象的属性又再恢复到原始状态,这并不是我们想要的。所以在产生对象副本的时候,构造函数不会被执行,被执行的是一个默认的构造函数。当函数执行完毕要返回的时候,对象副本会执行析构函数,如果你的析构函数是空的话,就不会发生什么问题,但一般的析构函数都是要完成一些清理工作,如释放指针所指向的内存空间。这时候问题就可能要出现了。假如你在构造函数里面为一个指针变量分配了内存,在析构函数里面释放分配给这个指针所指向的内存空间,那么在把对象传递给函数至函数结束返回这一过程会发生什么事情呢?首先有一个对象的副本产生了,这个副本也有一个指针,它和原始对象的指针是指向同块内存空间的。函数返回时,对象的析构函数被执行了,即释放了对象副本里面指针所指向的内存空间,但是这个内存空间对原始对象还是有用的啊,就程序本身而言,这是一个严重的错误。然而错误还没结束,当原始对象也被销毁的时候,析构函数再次执行,对同一块系统动态分配的内存空间释放两次是一个未知的操作,将会产生严重的错误。     上面说的就是我们会遇到的问题。解决问题的方法是什么呢?首先我们想到的是不要以传值的方式来传递参数,我们可以用传地址或传引用。没错,这样的确可以避免上面的情况,而且在允许的情况下,传地址或传引用是最好的方法,但这并不适合所有的情况,有时我们不希望在函数里面的一些操作会影响到函数外部的变量。那要怎么办呢?可以利用复制构造函数来解决这一问题。复制构造函数就是在产生对象副本的时候执行的,我们可以定义自己的复制构造函数。在复制构造函数里面我们申请一个新的内存空间来保存构造函数里面的那个指针所指向的内容。这样在执行对象副本的析构函数时,释放的就是复制构造函数里面所申请的那个内存空间。

     如果派生类定义了自己的复制构造函数,该复制构造函数一般应显式使用基类复制构造函数初始化对象的基类部分,否则效果是运行基类的默认构造函数初始化对象的基类部分。


     补充:
     编译器总是显式(自动)调用派生类对象基类部分的析构函数。对象撤销的顺序和构造的顺序相反,首先运行派生类的析构函数,然后按继承层次依次向上调用各基类的析构函数
    虚析构函数:删除动态分配对象的指针时,需要运行析构函数在释放对象的内存之前清除对象。处理继承层次中的对象时,指针的静态类型可能与被删除指针的动态类型不同,即可能删除实际指向派生类对象的基类类型指针。要保证运行适当的析构函数,基类中的析构函数必须为虚函数


评分

参与人数 1鱼币 +2 贡献 +1 收起 理由
鱼-鱼 + 2 + 1 感谢楼主无私奉献!

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2014-4-17 19:00:32 | 显示全部楼层
不讨论你的前置++重载返回只的问题。单单就这个程序来看,是编译器对常规语法进行了优化,编译器识别出
A operator++()
         {
                 i++;
                 
                return A(i);
         }
返回值是用来初始化b的
A b=++a;
所以,直接用带参构造函数来初始化b了,这样可以节省一次拷贝构造函数的调用

评分

参与人数 1鱼币 +2 贡献 +1 收起 理由
鱼-鱼 + 2 + 1

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2014-4-17 19:05:24 | 显示全部楼层
给你简单的分析下 A b=++a;  执行这条语句系统在干什么(反汇编)
首先 进入
       A operator++()
        {
                i++;      
                return A(i);
        }
此时i=2,当执行到  return A(i);   接着进入
A(int x)
{
     i=x;
     cout<<"带参数的函数执行中!"<<endl;
}

这时候++a就返回了一个无名对象,系统将无名对象的数据成员拷贝给对象 b ,该对象的数据成员 i=2 。利用无名对象初始化对象系统不会调用拷贝构造函数。
这是因为无名对象使用过后在整个程序中就失去了作用。

评分

参与人数 1鱼币 +2 贡献 +1 收起 理由
鱼-鱼 + 2 + 1 感谢楼主无私奉献!

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-12-26 00:00

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表