IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 构造函数与析构函数的问题总结 -> 正文阅读

[C++知识库]构造函数与析构函数的问题总结

目录

C++中有哪些构造函数

构造函数的定义

构造函数的分类

构造函数相关问题

问题一:什么情况下会调用拷贝构造函数?

问题二:赋值初始化和列表初始化的区别?

问题三:构造函数和析构函数的执行顺序 ?

问题四:构造函数能否声明为虚函数或者纯虚函数,析构函数呢?

问题五:构造函数、析构函数、可否声明为内联函数inline

问题六:构造函数和析构函数可以调用虚函数吗?

问题七:什么情况会自动生成默认构造函数?

问题八:为什么拷贝构造函数必须传引用不能传值?



C++中有哪些构造函数

构造函数的定义

构造函数被使用来对象创造时初始化工作,即在构造函数中为对象赋初值

C11前是不允许成员变量定义时就初始化的操作(C11之后是允许的,原理请参考C11中类成员变量定义时初始化问题),例如

class Solution {
public:
	int arg = 1; //C11之前是会报错  (C11标标准出来后在类中是允许这样初始化的)
}

因此就设计通过构造函数还在实例化对象时初始化成员变量。如下几种常见的构造函数

构造函数的分类

  • 默认构造函数
  • 列表初始化构造函数
  • 拷贝构造函数
  • 移动构造函数(move和右值引用)

程序例如:

#include <iostream>
using namespace std;

class Solution {
public:
	Solution(){//默认构造函数
			this->arg = 1;
			cout << "default constructor" << endl;
	}

	Solution(int num):arg(num){  //列表初始化构造函数
			cout << "list initialization constructor" << endl;
	} 

	Solution(const Soulution& s) { //拷贝构造函数
			this->arg = s.arg;
			cout << "copy constructor" << endl;
	}
	Solution(Solution &&s) : arg(s.arg) {  //移动构造函数    输入参数是一个右值,&&表示右值引用
			cout << "move constructor" << endl;
	}

public:
	int arg;
};

int main()
{
    cout << "-------------------------1-------------------------" << endl;
    Solution a;  //调用默认构造函数
    cout << "-------------------------2-------------------------" << endl;
    Solution b(1); //调用列表初始化
    cout << "-------------------------3-------------------------" << endl;
    Solution c(a);  //调用拷贝构造
    cout << "-------------------------4-------------------------" << endl;
    Solution d(move(a));  //使用move函数,将a对象转为右值,调用移动构造
}

输出结果:

构造函数相关问题

问题一:什么情况下会调用拷贝构造函数?

除了上面初始化类对象时通过引用传递对象会调用拷贝构造函数的情况,还有以下几种特殊情况也会调用拷贝构造函数。

情况1:使用“=”,用类的一个实例化对象去初始化另一个对象的时候

情况2:函数参数是类的对象,并且不是引用传递(此处是函数参数,不是类对象初始化)

情况3:函数的返回值是函数体内的局部类对象时 ,且返回方式是值传递,会在返回值的地方调用拷贝构造函数

例如:

#include <iostream>
using namespace std;

class Solution {
public:
    Solution(){//默认构造函数
        this->arg = 1;
        cout << "default constructor" << endl;
    }
    Solution(const Solution& s) { //拷贝构造函数
        this->arg = s.arg;
        cout << "copy constructor" << endl;
    }
public:
    int arg;
};
void function1(Solution s) {  //值传递,不是引用传递
    cout << "function1()" << endl;
}
int main()
{
    Solution a;
    cout << "-------------------------1-------------------------" << endl;
    Solution b = a; //情况1
    cout << "-------------------------2-------------------------" << endl;
    function1(b);  //情况2
    return 0;
}

结果:

从结果可以看到Solution b = a; 和function1(b);都调用了拷贝构造函数。

情况3这种情况,如下面这种形式:

Solution function2() { //函数的返回值是类对象
    cout << "function2()" << endl;
    Solution s;
    return s;
}

这种情况在不同的平台会有不同的结果,在Linux中使用g++时返回局部对象不会调用拷贝构造函数;在windows中使用visual studio 2019时,返回局部对象,且是值传递会调用拷贝构造函数。

问题二:赋值初始化和列表初始化的区别?

默认构造函数拷贝构造函数这类使用赋值初始化操作的是通过在函数体内进行赋值初始化;列表初始化在冒号后使用初始化列表进行初始化。 主要区别在于数据初始化的时间不同: 赋值初始化是在所有的数据成员被分配内存空间后才进行的。 列表初始化是给数据成员分配内存空间时就进行初始化,就是说分配一个数据成员只要冒号后有此数据成员的赋值表达式(此表达式必须是括号赋值表达式),就赋值。分配一个数据的空间就初始化赋值。

注意:列表初始化时成员变量的初始化顺序是以数据成员的声明顺序一致。与列表初始化的顺序无关

问题三:构造函数和析构函数的执行顺序 ?

构造函数:

作为一个继承了父类的派生子类,子类实例化对象时构造函数的执行顺序如下:

① 虚拟基类的构造函数(多个虚拟基类则按照继承的顺序执行构造函数)。

② 基类的构造函数(多个普通基类也按照继承的顺序执行构造函数)。

③派生类类型的成员对象的构造函数(按照初始化顺序)

④ 派生类自己的构造函数。

析构函数:

① 调用派生类的析构函数;

② 调用派生类成员类对象的析构函数;

③ 调用基类的析构函数。

④ 调用虚拟基类的析构函数。

问题四:构造函数能否声明为虚函数或者纯虚函数,析构函数呢?

构造函数:

构造函数不能定义为虚函数。在构造函数中可以调用虚函数,不过此时调用的是正在构造的类中的虚函数,而不是子类的虚函数,因为此时子类尚未构造好。

原因:虚函数对应一个vtable(虚函数表),类中存储一个vptr指向这个vtable。如果构造函数是虚函数,就需要通过vtable调用,可是对象没有初始化就没有vptr,无法找到vtable,所以构造函数不能是虚函数。

析构函数:

析构函数可以为虚函数,并且一般情况下基类析构函数要定义为虚函数。

原因:从上面析构函数的调用顺序可知,首先要调用派生子类的析构函数。因此只有在基类析构函数定义为虚函数时,调用操作符delete销毁指向对象的基类指针时,才能准确调用派生类的析构函数(从该级向上按序调用虚函数),才能准确销毁数据。

析构函数可以是纯虚函数,含有纯虚函数的类是抽象类,此时不能被实例化。但派生类中可以根据自身需求重新改写基类中的纯虚函数。

问题五:构造函数、析构函数、可否声明为内联函数inline

首先,将这些函数声明为内联函数,在语法上没有错误。但是《Effective C++》指出:将构造函数和析构函数声明为inline是没有什么意义的,编译器并不真正对声明为inline的构造和析构函数进行内联操作,因为编译器会在构造和析构函数中添加额外的操作(申请/释放内存,构造/析构对象等),致使构造函数/析构函数并不像看上去的那么精简,因此不会使用inline功能。

问题六:构造函数和析构函数可以调用虚函数吗?

在C++中,可以调用,但不提倡

① 我们调用虚函数,一般就是使用其动态联编的功能。但是构造函数和析构函数调用虚函数时都不使用动态联编,如果在构造函数或析构函数中调用虚函数,则运行的是为构造函数或析构函数自身类型定义的版本;

② 构造函数时,因为父类对象会在子类之前进行构造,此时子类部分的数据成员还未初始化,因此C++不会进行动态联编;

③析构函数是用来销毁一个对象的,在销毁一个对象时,先调用子类的析构函数,然后再调用基类的析构函数。所以在调用基类的析构函数时,派生类对象的数据成员已经销毁,也没有使用动态联编。

问题七:什么情况会自动生成默认构造函数?

前提:如果类没有构造函数,编译器不一定会生成默认的构造函数,只有在构造函数真正被需要的时候才会生成;

① 如果一个类没有任何构造函数,但它含有一个带有默认构造函数的成员对象,那么在实例化该类时,编译器就为该类合成出一个默认构造函数。 原因:如果一个类A含有多个成员类对象的话,编辑器在给A类生成无参的默认构造函数的初始化列表的位置调用类对象的构造函数,完成对对象成员的初始化。且必须按照类对象在类A中的声明顺序进行;

class A {
public:
	A() {//默认构造函数
	
		cout << "A()" << endl;
	}
};
class B {
public:
	A a;   //含有A类的成员对象
	int num;
};
int main() {
	B b;//实例化B需要调用A类的默认构造函数。没有给出显示定义的构造函数,但是编辑器会自动生成一个默认(无参)的构造函数
	return 0;
}

② 带有默认构造函数的基类,如果一个没有构造函数的派生类继承自一个带有默认构造函数基类,那么该派生类会合成一个构造函数调用上一层基类的默认构造函数;

class Base {
public:
	Base() {  //默认构造函数
		cout << "Base()" << endl;
	}
};

class Son:public Base {
public:
	int d;  //没有构造函数
};
int main()
{
	Son s;  //实例化Son,编译器合成的默认构造函数
	return 0;
}

③带有一个虚函数的类

原因:含有虚函数的类都拥有一个虚指针vptr。编译器需要设置vptr。因此需要生成默认构造函数,设置vptr的初值。

④ 带有一个虚基类的类

问题八:为什么拷贝构造函数必须传引用不能传值?

例如:

Solution(const Soulution& s) { //拷贝构造函数  传递本类对象必须是引用
			this->arg = s.arg;
			cout << "copy constructor" << endl;
	}


Solution(Soulution s) { //传递本类对象非引用传递,错误
			this->arg = s.arg;
			cout << "copy constructor" << endl;
	}

原因:如果用传值的方式进行传参数,那么构造实参 ”Soulution s“ 需要调用拷贝构造函数,而拷贝构造函数需要传递实参,实参中又要调用拷贝构造函数,会一直递归。所以拷贝构造不允许使用值传递。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-24 09:11:57  更:2022-04-24 09:12:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 21:44:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码