C/C++知识点:复制构造函数
Vivian 2018-06-04 来源 : 阅读 768 评论 0

摘要:本文主要向大家介绍了C/C++知识点的复制构造函数,以下代码全都是在VS2015的Debug模式下运行的,使用其他编译器可能会结果不一样,比如g++就不是这样。希望对大家学习C/C++知识点有所帮助。

    本文主要向大家介绍了C/C++知识点的复制构造函数,以下代码全都是在VS2015的Debug模式下运行的,使用其他编译器可能会结果不一样,比如g++就不是这样。希望对大家学习C/C++知识点有所帮助。

先看下面的代码:

//code 1
#include <iostream>
 
using namespace std;
 
class Resource {
public:
    Resource() {
        cout << "Resource Constructor!" << endl;
    }
    ~Resource() {
        cout << "Resource Destructor!" << endl;
    }
};
 
class Test {
public:
    Test() {
        p = new Resource();
        cout << "Test Constructor!" << endl;
    }
    Test(Test& t) {
        p = new Resource();
        cout << "Test Copy Constructor!" << endl;
    }
     ~Test() {
        if (p != nullptr) {
            cout << "p is not null!" << endl;
            delete p;
        }
        else {
            cout << "p is null!" << endl;
        }
        cout << "Test Destructor!" << endl;
    }
 
    Resource* p;
};
 
 
 
Test goo() {
    Test t;
    return t;
}
 
int main() {
    goo();
    cout<<"end"<<endl; pre="" return=""><p>在我的VS2015上以Debug模式运行的话,其运行结果是:</p><blockquote><p>Resource Constructor!
 
  Test Constructor!
 
  Resource Constructor!
 
  Test Copy Constructor!
 
  p is not null!
 
  Resource Destructor!
 
  Test Destructor!
 
  p is not null!
 
  Resource Destructor!
 
  Test Destructor!
 
  end</p></blockquote><p>这里面的流程:
 
1. 首先调用Test的构造函数构造t,
 
2. 然后使用其复制构造函数构造foo函数的返回值,
 
3. 然后离开foo函数时,调用Test的析构函数析构变量t,
 
4. 在main函数中调用goo函数返回值的析构函数
 
5. 打印 end
 
6. 离开main函数</p><p>这里可见,goo函数返回的临时值在第一次使用后就立刻被析构了,这是一个右值。同时可以看到,这里Resource被分配了两次,分别是步骤2和步骤3中构造的。</p><p>下面修改一下代码:</p><pre class="brush:java;">// code 2
#include <iostream>
 
using namespace std;
 
class Resource {
public:
    Resource() {
        cout << "Resource Constructor!" << endl;
    }
    ~Resource() {
        cout << "Resource Destructor!" << endl;
    }
};
 
class Test {
public:
    Test() {
        p = new Resource();
        cout << "Test Constructor!" << endl;
    }
    Test(Test& t) {
        p = new Resource();
        cout << "Test Copy Constructor!" << endl;
    }
     ~Test() {
        if (p != nullptr) {
            cout << "p is not null!" << endl;
            delete p;
        }
        else {
            cout << "p is null!" << endl;
        }
        cout << "Test Destructor!" << endl;
    }
 
    Resource* p;
};
 
Test goo() {
    Test t;
    return t;
}
 
int main() {
    Test t(goo());//change
    cout<<"end"<<endl; pre="" return=""><p>在没运行之前,我觉得运行结果应该是这样:</p><blockquote><p>Resource Constructor!
 
  Test Constructor!
 
  Resource Constructor!
 
  Test Copy Constructor!
 
  p is not null!
 
  Resource Destructor£?
 
  Test Destructor!
 
  Test Copy Constructor!
 
  p is not null!
 
  Resource Destructor£?
 
  Test Destructor!
 
  end
 
  p is not null!
 
  Resource Destructor£?
 
  Test Destructor!</p></blockquote><p>我当时内心想的流程是这样的:
 
1. 首先goo函数内部构造t,调用其构造函数
 
2. 然后使用复制构造函数构造goo函数的返回值,调用复制构造函数
 
3. 然后对goo内部的t调用析构函数
 
4. 在main函数内部,对t调用复制构造函数(移动复制构造函数后面再说),复制参数就是goo函数的返回值
 
5. 对goo函数的返回值调用析构函数
 
6. 打印 end
 
7. 离开main函数,调用main函数中的t的析构函数</p><p>而实际上,其运行结果如下:</p><blockquote><p>Resource Constructor!
 
  Test Constructor!
 
  Resource Constructor!
 
  Test Copy Constructor!
 
  p is not null!
 
  Resource Destructor£?
 
  Test Destructor!
 
  end
 
  p is not null!
 
  Resource Destructor£?
 
  Test Destructor!</p></blockquote><p>跟我之前预期的不一样,感觉像是没有对goo函数的返回结果进行构造,而是直接用返回值(这里不准确,感觉返回值都没有构造,感觉像是直接用goo里面的t)对main函数中的t进行了复制构造。在我之前的译文C++中的右值引用中,里面有这样一句话:</p><blockquote><p>任何一个现代编译器都会对原始的那个函数定义应用返回值优化。换句话说,编译器会直接在foo返回值的位置构造一个X对象,而不是在内部构造一个X对象然后将它复制出去。</p></blockquote><p>如果按照这个规则,代码2很好解释,但是代码1就很难解释,因为代码1的运行结果显示,真的在函数内部构造一个Test对象,然后对函数的返回值进行了复制。关于这个问题,我现在还不太清楚,感觉需要查看汇编来查看。汇编我以后再写。</p><p>言归正传,下面再修改一些代码,对Test类增加移动语义:</p><pre class="brush:java;">// code 3
#include <iostream>
 
using namespace std;
 
class Resource {
public:
    Resource() {
        cout << "Resource Constructor!" << endl;
    }
    ~Resource() {
        cout << "Resource Destructor!" << endl;
    }
};
 
class Test {
public:
    Test() {
        p = new Resource();
        cout << "Test Constructor!" << endl;
    }
    Test(Test& t) {
        p = new Resource();
        cout << "Test Copy Constructor!" << endl;
    }
    Test(Test&& t) {//change
        p = t.p;
        t.p = nullptr;
        cout << "Test Move Copy Constructor!" << endl;
    }
    Test& operator=(Test&& t) {//change
        Resource *ptr = p;
        p = t.p;
        t.p = ptr;
        cout << "Test Move Assignment Operator!" << endl;
        return *this;
    }
     ~Test() {
        if (p != nullptr) {
            cout << "p is not null!" << endl;
            delete p;
        }
        else {
            cout << "p is null!" << endl;
        }
        cout << "Test Destructor!" << endl;
    }
 
    Resource* p;
};
int main() {
    goo();
    cout<<"end"<<endl; pre="" return=""><p>这里对类Test加入了移动复制构造函数,运行结果如下:</p><blockquote><p>Resource Constructor!
 
  Test Constructor!
 
  Test Move Copy Constructor!
 
  p is null!
 
  Test Destructor!
 
  p is not null!
 
  Resource Destructor£?
 
  Test Destructor!
 
  end</p></blockquote><p>这里面的流程是:
 
1. 首先使用Test的构造函数构造t
 
2. 然后使用Test的移动复制构造函数构造goo函数的返回值
 
3. 对goo函数中的t进行析构
 
4. 在main函数中对goo函数的返回值进行析构
 
5. 打印 end
 
6. 离开main函数</p><p>这里可以看出,在构造goo函数的返回值时,优先使用了移动复制构造函数,这样的话,减少了一次内部资源的分配(Resource只分配了一次),其余和代码1都一样。</p><p>下面再修改一下代码:</p><pre class="brush:java;">// code 4
#include <iostream>
 
using namespace std;
 
class Resource {
public:
    Resource() {
        cout << "Resource Constructor!" << endl;
    }
    ~Resource() {
        cout << "Resource Destructor!" << endl;
    }
};
 
class Test {
public:
    Test() {
        p = new Resource();
        cout << "Test Constructor!" << endl;
    }
    Test(Test& t) {
        p = new Resource();
        cout << "Test Copy Constructor!" << endl;
    }
    Test(Test&& t) {
        p = t.p;
        t.p = nullptr;
        cout << "Test Move Copy Constructor!" << endl;
    }
    Test& operator=(Test&& t) {
        Resource *ptr = p;
        p = t.p;
        t.p = ptr;
        cout << "Test Move Assignment Operator!" << endl;
        return *this;
    }
     ~Test() {
        if (p != nullptr) {
            cout << "p is not null!" << endl;
            delete p;
        }
        else {
            cout << "p is null!" << endl;
        }
        cout << "Test Destructor!" << endl;
    }
 
    Resource* p;
};
int main() {
    Test t;//change
    t = goo();//change
    cout<<"end"<<endl; pre="" return=""><p>这里的运行结果是:</p><blockquote><p>Resource Constructor!
 
  Test Constructor!
 
  Resource Constructor!
 
  Test Constructor!
 
  Test Move Copy Constructor!
 
  p is null!
 
  Test Destructor!
 
  Test Move Assignment Operator!
 
  p is not null!
 
  Resource Destructor£?
 
  Test Destructor!
 
  end
 
  p is not null!
 
  Resource Destructor£?
 
  Test Destructor!</p></blockquote><p>读者可以按照上面的思想自己思考流程。
 
在代码4的基础上,如果我将移动赋值操作符重载改成下面这样:</p><pre class="brush:java;">Test& operator=(Test&& t) {
        p = t.p;
        out << "Test Move Assignment Operator!" << endl;
        return *this;</pre>
<p>运行后会发生什么?</p>
</endl;></iostream></pre>
</endl;></iostream></pre>
</endl;></iostream></pre>
</endl;></iostream>

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标编程语言C/C+频道!

本文由 @Vivian 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程