简 述: 分析 C++ Class 的六个特殊成员函数 wiki ,并且手动实现一番;
- 默认构造函数
- 析构函数
- 复制构造函数
- 复制赋值运算符
- 移动构造函数
- 移动赋值运算符
[TOC]
本文初发于 “偕臧的小站“,同步转载于此。
原理
特殊成员函数是类(或结构)成员函数,在某些情况下,编译器会自动为你生成。
- default constructor: 通常没有参数,但可以具有带默认值的参数。
- destructor: 销毁对象的前一刻执行清理
- copy constructor: 对新建的的对象进行初始化,形参是
const T&
- copy assignment constructor: 对已有的对象进行赋值,形参是
const T&
- move constructor: 对新建的的对象进行初始化,形参是
T &&
- move assignment constructor: 对已有的对象进行赋值,形参是
T &&
move 的函数相对于 copy 的函数没有
const
copy 是属完整的再复制拷贝一份;move 是对将亡的右值进行指针互换、节省空间提升效率
”复制 / 移动构造函数“ 属于新建对象,无需判断两者是否相等;”复制 / 移动赋值运算符“ 属于已有的对象进行赋值,赋值时需要先判断两者是否相等。
若有深拷贝时,”复制 / 移动赋值运算符“ 除了判断相等、数值是否有效;在拷贝字节前,还要先释放旧资源
只有当类存储了需要释放的系统资源的句柄,或拥有其指向的内存的指针时,你才需要定义自定义 ”析构函数“
三五法则 cppreference.com
代码
💻 win10 22H2
📎 Visual Studio 2019
📎 C++17
见 SpecialMembers.h
/*******************************************************************
* Copyright (c) 2022~2023 XMuli All rights reserved.
* Description: C++ 类的六个特殊成员函数
******************************************************************/
#pragma once
#include <iostream>
#include <utility>
using namespace std;
class A
{
public:
A() : m_ptr(nullptr) {
std::cout << "default constructor" << endl;
}
A(const char* s) : m_ptr(nullptr) {
std::cout << "no-default-val constructor" << endl;
if (s) {
auto n = std::strlen(s) + 1;
m_ptr = new char[n];
std::memcpy(m_ptr, s, n);
}
}
A(const A& other)
: m_ptr(nullptr) {
std::cout << "copy constructor" << endl;
if (other.m_ptr) {
auto n = std::strlen(other.m_ptr) + 1;
m_ptr = new char[n];
std::memcpy(m_ptr, other.m_ptr, n);
}
}
A& operator = (const A& other) {
std::cout << "copy assignment constructor" << endl;
if (this != &other) {
delete[] m_ptr; // Free the existing resource. 重点 !!!
if (other.m_ptr != nullptr) {
auto n = std::strlen(other.m_ptr) + 1;
m_ptr = new char[n];
std::memcpy(m_ptr, other.m_ptr, n);
}
}
return *this;
}
A(A&& other) noexcept // 行参无 const
: m_ptr(nullptr) {
std::cout << "move constructor" << endl;
if (other.m_ptr)
m_ptr = std::move(other.m_ptr);
other.m_ptr = nullptr;
}
A& operator=(A&& other) noexcept { // 行参无 const
std::cout << "move assignment constructor" << endl;
if (this != &other) {
delete[] m_ptr; // Free the existing resource.
if (other.m_ptr)
m_ptr = std::move(other.m_ptr);
other.m_ptr = nullptr;
}
return *this;
}
~A() {
std::cout << "destructor" << endl;
if (m_ptr)
delete[] m_ptr;
}
private:
char* m_ptr;
};
A fn() {
A t("A fun()");
return t;
}
int main()
{
A a1("a1"); // default constructor
A a2(a1); // copy constructor
A a3 = a1; // copy constructor
a1 = a3; // copy assignment constructor
std::cout << "----------------------------\n\n";
//fn(); // function returning a A object
A a5 = std::move(a1); // move constructor
A a6; // default constructor
a6 = std::move(a1); // move assignment constructor
std::cout << "Hello World!\n";
return 0;
}
/*************************** 运行结果 *******************************
* no-default-val constructor
copy constructor
copy constructor
copy assignment constructor
----------------------------
move constructor
default constructor
move assignment constructor
Hello World!
destructor
destructor
destructor
destructor
destructor
******************************************************************/
拷贝构造函数是 A a2(a1);
形式,还可以是 A a3 = a1;
这种形式。关键看是新创建对象还是已有的对象赋值。move 的函数赋值,借助 std::move()
将左值转换为右值。
系列
QtExamples 的 【Studio】
欢迎 star
⭐ 和 fork
🍴 这个系列的 C++ / QT / DTK
学习,附学习由浅入深的目录,这里你可以学到如何亲自编写这类软件的经验,这是一系列完整的教程,并且永久免费!”