构造函数的调用场景--构造函数与拷贝构造函数、移动构造函数之辨
在Scott Meyers的著作《Effective C++》条款5中,明确指出要“了解C++默默编写并调用哪些函数”。这里通过一个简短的C++代码,阐述哪些场景调用构造函数,哪些场景不调用构造函数。
目录
场景1
场景2
场景3
场景4
甲方:
乙方:
真实情况:
源码
pro文件中加入编译选项-fno-elide-constructors
头文件
cpp文件:
结果:
场景1
1.1 对于 构造函数(输入参数) 这样的函数,且这个构造函数没有用explicit修饰,则如下场景将调用构造函数:
类型名 变量 = 输入参数;
或
类型名 变量 = {输入参数...};
1.2 假如采用explicit修饰构造函数,则以上的语句无法通过编译。
场景2
对于 构造函数(输入参数) 这样的函数,不论函数没有用explicit修饰。此时调用构造函数。
类型名 变量(输入参数);
场景3
如下语句调用拷贝构造函数:
类型名 变量 = 另一个已经存在的同类型变量
场景4
这是有争议的场景。
类型名 变量 = 该类型构造函数(输入变量...);
甲方:
对于 构造函数(输入参数) 这样的函数,不论函数没有用explicit修饰。采用如上语句,在vs2013和g++ 下运行,结果一致:发现程序只调用构造函数。
乙方:
但是chatgpt认为程序应先调用构造函数,再调用拷贝构造函数。
真实情况:
实测结果与编译器优化有关。在vs2013中,返回值优化(RVO)是无法关闭的(CPP11-右值引用 - jesonwoo - 博客园 (cnblogs.com)),所以vs2013的测试结果是只调用构造函数。
使用g++编译时,默认也打开了RVO。所以使用g++的测试结果也是只调用构造函数。但是可以通过编译选项-fno-elide-constructors来关闭RVO。关闭后,打印发现场景4调用了一次构造函数,和一次移动构造函数--因为等号右端是一个右值,触发移动构造函数。
也就是说,在关闭RVO的情况下,chatgpt说的是对的。
源码及打印效果见下面。
源码
源码采用qt编写。
pro文件中加入编译选项-fno-elide-constructors
#-------------------------------------------------
#
# Project created by QtCreator 2023-03-26T22:37:12
#
#-------------------------------------------------
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
QMAKE_CXXFLAGS += -fno-elide-constructors
TARGET = str
TEMPLATE = app
SOURCES += main.cpp\
mainwindow.cpp
HEADERS += mainwindow.h
FORMS += mainwindow.ui
头文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
};
class str
{
public:
str() = delete;
str(char *, int i);
str(const str &);
str(str &&);
str & operator=(const str &);
str & operator=(str &&);
char arr[1024];
};
#endif // MAINWINDOW_H
cpp文件:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
str s1 = {"123", 1};//场景1
str s2("123", 1);//场景2
str s3 = s2;//场景3
str s4 = str("234", 2);//场景4
}
MainWindow::~MainWindow()
{
delete ui;
}
str::str(char * pStr, int i)
{
strcpy(arr, pStr);
qDebug()<<"constructor";
qDebug()<<i;
}
str::str(const str & other)
{
strcpy(arr, other.arr);
qDebug()<<"copy constructor";
}
str::str(str && other)
{
strcpy(arr, other.arr);
qDebug()<<"move constructor";
}
str & str::operator=(const str & other)
{
strcpy(arr, other.arr);
qDebug()<<"copy assign";
return *this;
}
str & str::operator=(str && other)
{
strcpy(arr, other.arr);
qDebug()<<"move assign";
return *this;
}