C++构造函数的一些注意事项总结

 更新时间:2021年11月01日 14:47:21   作者:学渣的C/C++  
构造函数是创建类对象,并且在创建完成前,对类进行初始化的特殊函数,下面这篇文章主要给大家介绍了关于C++构造函数的一些注意事项,需要的朋友可以参考下

1、匿名对象

首先应该明确匿名对象,匿名对象是之没有对象名,调用完构造函数后即析构的对象。下面通过代码捕捉类的构造函数和析构函数,以进行说明:

#include <iostream>
using namespace std;

class Solution{
public:
    Solution(int a, int b):m_num1(a), m_num2(b) {
        cout << "有参构造函数的调用" << endl;
    };
    Solution(const Solution& s):m_num1(s.m_num1), m_num2(s.m_num2){
        cout << "拷贝构造函数的调用" << endl;
    } 
    ~Solution(){
        cout << "析构函数的调用" << endl;
    }
private:
    int m_num1;
    int m_num2;
};

int main()
{
    Solution(8,9); // Solution(8,9) 匿名对象
    
    system("pause");
    return 0;
}

代码运行结果为:

通过代码运行结果可以看到,创建匿名对象的时候,调用了类的构造函数,随后立即调用了析构函数。我们可以直接利用匿名对象进行初始化类的成员的初始化,代码如下:

#include <iostream>
using namespace std;

class Solution{
public:
    Solution(int a, int b):m_num1(a), m_num2(b) {
        cout << "有参构造函数的调用" << endl;
    };
    Solution(const Solution& s):m_num1(s.m_num1), m_num2(s.m_num2){
        cout << "拷贝构造函数的调用" << endl;
    } 
    ~Solution(){
        cout << "析构函数的调用" << endl;
    }

    int m_num1;
    int m_num2;
};

int main()
{
    Solution s1(Solution(8,9));
    // Solution s1 = Solution(8,9); //显式 Solution(8,9) 匿名对象初始化类成员
    // Solution s1 = {8,9};        //{8,9}等价于Solution(8,9)
    cout << "s1.m_num1 = " << s1.m_num1 << endl;
    cout << "s1.m_num2 = " << s1.m_num2 << endl;   
    system("pause");
    return 0;
}

运行结果如下:

代码调用了一次构造函数,可见匿名对象可以初始化类成员,就是将匿名对象转化成了s1对象,这里需要与拷贝构造函数区分开:

#include <iostream>
using namespace std;

class Solution{
public:
    Solution(int a, int b):m_num1(a), m_num2(b) {
        cout << "有参构造函数的调用" << endl;
    };
    Solution(const Solution& s):m_num1(s.m_num1), m_num2(s.m_num2){
        cout << "拷贝构造函数的调用" << endl;
    } 
    ~Solution(){
        cout << "析构函数的调用" << endl;
    }

    int m_num1;
    int m_num2;
};

int main()
{
    Solution s1(10,11);
    Solution s2(s1);
    cout << "s2.m_num1 = " << s2.m_num1 << endl;
    cout << "s2.m_num2 = " << s2.m_num2 << endl;
    system("pause");
    return 0;
}

代码运行结果为:

对比以上两个代码可以发现,通过匿名对象初始化类成员并不是拷贝构造,只是一种替换,通过匿名对象初始化类成员并不会调用拷贝构造函数。

2、拷贝构造函数的调用时机

今年秋招笔试题最爱考查构造函数的调用时机,通常会结合继承和多态来考察,这里先说明一下拷贝构造函数的调用时机,后面再详细说明带继承和多态的构造函数调用时机。

一、使用一个已经创建的对象来初始化一个新对象,如上面的代码,创建对象s1的时候调用了有参构造函数,通过s1来初始化s2的时候调用了拷贝构造。

二、函数的参数是需要值传递的对象的时候

三、函数返回对象的时候

下面通过代码验证,当函数的参数是一个需要值传递的对象的情况:

#include <iostream>
using namespace std;

class Solution{
public:
    Solution(int a, int b):m_num1(a), m_num2(b) {
        cout << "有参构造函数的调用" << endl;
    };
    Solution(const Solution& s):m_num1(s.m_num1), m_num2(s.m_num2){
        cout << "拷贝构造函数的调用" << endl;
    } 
    ~Solution(){
        cout << "析构函数的调用" << endl;
    }
public:
    int m_num1;
    int m_num2;
};

void showClassNum(Solution s) {
    cout << s.m_num1 << endl;
    cout << s.m_num2 << endl;
}

int main()
{
    Solution s1(10,11);
    showClassNum(s1);
    system("pause");
    return 0;
}

代码运行结果为:

通过代码运行结果可以看到,s1对象调用了有参构造函数,当把s1传给函数的参数s的时候调用了拷贝构造函数,函数执行完调用了s对象的析构函数。

当函数的返回值是一个对象的时候,也会调用拷贝构造函数:

#include <iostream>
using namespace std;

class Solution{
public:
    Solution(int a, int b):m_num1(a), m_num2(b) {
        cout << "有参构造函数的调用" << endl;
    };
    Solution(const Solution& s):m_num1(s.m_num1), m_num2(s.m_num2){
        cout << "拷贝构造函数的调用" << endl;
    } 
    ~Solution(){
        cout << "析构函数的调用" << endl;
    }

    void changeNum(int a, int b) {
        m_num1 = a;
        m_num2 = b;
    }

    void showNum() {
        cout << "m_num1 = " << m_num1 << endl;
        cout << "m_num2 = " << m_num2 << endl;
    }
public:
    int m_num1;
    int m_num2;
};

Solution clearClassNum(Solution s) {
    s.changeNum(0,0);
    return s;
}

int main()
{
    Solution s1 (10,10);
    s1 = clearClassNum (s1);
    s1.showNum();
    system("pause");
    return 0;
}

代码运行结果为:

从代码运行结果可以看出来,函数传参的时候调用了拷贝构造,然后函数返回一个对象的时候的也调用了拷贝构造。

需要注意的是,函数要返回对象的时候,不要使用引用的方式返回,因为函数的内的变量存放在堆栈区,在函数执行完毕后就会释放这块内存,引用就会出现问题。

3、深拷贝和浅拷贝

面试的时候比较喜欢问的问题,首先浅拷贝就是我们常用的拷贝,实现了对象成员的拷贝,深拷贝就是在堆区申请空间,然后再进行拷贝操作,浅拷贝可以由编译器完成,但是深拷贝需要我们自己完成,就是有在堆区开辟的内存,就一定要自己提供拷贝构造函数,防止浅拷贝带来的重复内存释放问题。代码验证如下:

#include <iostream>
using namespace std;

class Solution{
public:
    Solution(int a, int b):m_num1(a), pm_num2(new int(b)) {
        cout << "有参构造函数的调用" << endl;
    };
      //如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区问题,导致程序出错
    Solution(const Solution& s):m_num1(s.m_num1), pm_num2(new int(*s.pm_num2)) {
        cout << "拷贝构造函数的调用" << endl;
    }
    ~Solution(){
        cout << "析构函数的调用,释放堆区申请的内存" << endl;
        if (pm_num2 != nullptr) {
            delete pm_num2;
        }
    }

    void changeNum(int a, int b) {
        m_num1 = a;
        *pm_num2 = b;
    }

    void showNum() {
        cout << "m_num1 = " << m_num1 << endl;
        cout << "m_num2 = " << *pm_num2 << endl;
    }
public:
    int m_num1;
    int* pm_num2;
};

void func()
{
    Solution s1 (10,10);
    Solution s2(s1);
    s2.showNum();
}

int main()
{

    func();
    system("pause");
    return 0;
}

代码运行结果为:

总结

到此这篇关于C++构造函数注意事项的文章就介绍到这了,更多相关C++构造函数注意事项内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Qt实现简单TCP服务器

    Qt实现简单TCP服务器

    这篇文章主要为大家详细介绍了Qt实现简单TCP服务器,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • 用C语言实现简单扫雷游戏

    用C语言实现简单扫雷游戏

    这篇文章主要为大家详细介绍了用C语言实现简单扫雷游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-07-07
  • C语言 野指针与空指针专篇解读

    C语言 野指针与空指针专篇解读

    全网最接地气的C语言野指针介绍,此处对于野指针与空指针知识点做一些简要的介绍,作者实属初学,写博客也是作者学习的一个过程,难免文章中有内容理解不到位或者有不当之处,还请朋友们不吝指正,希望大家多多给予支持,赠人玫瑰,手有余香
    2021-11-11
  • C语言中基础小问题详细介绍

    C语言中基础小问题详细介绍

    这篇文章详细介绍了C语言中基础小问题,有需要的朋友可以参考一下
    2013-10-10
  • C++中string的模拟实现

    C++中string的模拟实现

    这篇文章主要为大家详细介绍了C++中string的模拟实现,感兴趣的小伙伴们可以参考一下
    2016-08-08
  • VC++ 2019 "const char*"类型的实参与"LPCTSTR"类型的形参不兼容解决

    VC++ 2019 "const char*"类型的实参与"LPCTSTR"

    这篇文章主要给大家介绍了关于VC++ 2019 "const char*"类型的实参与"LPCTSTR"类型的形参不兼容的解决方法,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2023-03-03
  • 解析wprintf 中使用%I64d格式化输出LONGLONG的详细介绍

    解析wprintf 中使用%I64d格式化输出LONGLONG的详细介绍

    本篇文章是对wprintf 中使用%I64d格式化输出LONGLONG进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • Qt5.14.2使用虚拟键盘的关键代码

    Qt5.14.2使用虚拟键盘的关键代码

    对于Qwidget程序,使用qtvirtualkeyboard弹出键盘之后,键盘会浮于表面。使用VirtualkeyboardPushView模块,自动根据情况把输入视图往上面推移,这篇文章主要介绍了Qt5.14.2使用虚拟键盘的关键代码,需要的朋友可以参考下
    2022-09-09
  • C++中LibCurl库使用流程及配置详解

    C++中LibCurl库使用流程及配置详解

    libcurl是一个跨平台的开源网络传输库,它支持许多协议,包括HTTP、HTTPS、FTP、FTPS以及许多其他协议和文件传输方式,本文给大家详细介绍了C++中LibCurl库使用流程及配置,需要的朋友可以参考下
    2024-02-02
  • C语言程序环境和预处理详解分析

    C语言程序环境和预处理详解分析

    大家有没有想过,在vs2019的编译器上只要按下Ctrl+F5,一个test.c的源程序就能变成一个.exe的可执行程序,这其中是如何通过编译产生的呢,本章就和大家一起把其中的知识和重点的预处理一起学习一下
    2022-03-03

最新评论