一起聊聊C++中的特殊成员函数

下面我将详细讲解一下C++中特殊成员函数的相关知识。

一起聊聊C++中的特殊成员函数

什么是特殊成员函数

在C++中,除了一些普通的成员函数,还有一些被称为特殊成员函数的成员函数。这些特殊成员函数包括:

  • 默认构造函数
  • 拷贝构造函数
  • 移动构造函数
  • 拷贝赋值运算符
  • 移动赋值运算符
  • 析构函数

这些函数被称为特殊成员函数的原因是它们都在特定的情况下被自动调用,无需显式调用。

默认构造函数

默认构造函数是一种没有参数的构造函数,它在创建对象时被自动调用,用来初始化对象的成员变量。如果我们没有定义默认构造函数,那么编译器会为我们自动生成一个默认构造函数。

class MyClass {
public:
    MyClass() {
        // 构造函数的实现
    }
};

需要注意的是,如果我们在类中定义了其他构造函数,那么编译器就不会为我们生成默认构造函数,这时如果我们需要默认构造函数,就需要显式地定义它。

也可以把默认构造函数定义为被删除的函数,这样编译器在需要默认构造函数时就不能自动生成。

class MyClass {
public:
    MyClass() = delete;
};

拷贝构造函数

拷贝构造函数是一种特殊的构造函数,用来在创建新对象时,将一个已有的对象的值赋值给新对象。拷贝构造函数的参数为该类的另一个对象的引用。

class MyClass {
public:
    MyClass(const MyClass& other) {
        // 拷贝构造函数的实现
    }
};

需要注意的是,如果我们不定义拷贝构造函数,编译器会为我们自动生成一个默认的拷贝构造函数,该函数会对类的每个成员变量执行浅拷贝操作。如果我们的类中有指针类型的成员变量,就需要显式定义拷贝构造函数,以执行深拷贝操作。

class MyClass {
public:
    MyClass(const MyClass& other) {
        this->p = new int(*other.p); // 深拷贝
    }

private:
    int* p;
};

移动构造函数

移动构造函数也是一种特殊的构造函数,它在创建新对象时使用一个临时对象的值,避免了拷贝操作,提高了程序的效率。移动构造函数的参数为一个右值引用。

class MyClass {
public:
    MyClass(MyClass&& other) {
        // 移动构造函数的实现
    }
};

需要注意的是,如果我们不定义移动构造函数,编译器会为我们自动生成一个默认的移动构造函数,该函数会对类的每个成员变量执行浅拷贝操作。如果我们的类中有指针类型的成员变量,就需要显式定义移动构造函数,以执行浅拷贝操作。

class MyClass {
public:
    MyClass(MyClass&& other) {
        // 移动构造函数的实现
        this->p = other.p;  // 浅拷贝
        other.p = nullptr;
    }

private:
    int* p;
};

拷贝赋值运算符

拷贝赋值运算符用来在已经存在的对象之间赋值。拷贝赋值运算符的参数为该类的另一个对象的引用。在实现拷贝赋值运算符时,需要注意要进行自我赋值的判断。

class MyClass {
public:
    MyClass& operator=(const MyClass& other) {
        // 拷贝赋值运算符的实现
        if (this == &other) {  // 自我赋值
            return *this;
        }
        delete this->p;  // 释放原有的内存
        this->p = new int(*other.p);  // 深拷贝
        return *this;
    }

private:
    int* p;
};

移动赋值运算符

移动赋值运算符也是在已经存在的对象之间赋值,它使用一个临时对象的值,避免了拷贝操作,提高了程序的效率。移动赋值运算符的参数为一个右值引用。

class MyClass {
public:
    MyClass& operator=(MyClass&& other) {
        // 移动赋值运算符的实现
        delete this->p;  // 释放原有的内存
        this->p = other.p;  // 浅拷贝
        other.p = nullptr;
        return *this;
    }

private:
    int* p;
};

析构函数

析构函数是对于一个对象而言最后会被执行的函数,用来释放对象所占用的资源。当对象在程序中被删除,或销毁时,会自动调用析构函数。

class MyClass {
public:
    ~MyClass() {
        // 析构函数的实现
        delete this->p;
    }

private:
    int* p;
};

需要注意的是,如果我们的类中有指针类型的成员变量,需要在析构函数中显式地释放内存,否则会造成内存泄漏。

示例

下面是一个使用特殊成员函数示例:

#include <iostream>

class MyClass {
public:
    MyClass() {
        std::cout << "调用默认构造函数" << std::endl;
        this->p = new int(0);
    }

    MyClass(const MyClass& other) {
        std::cout << "调用拷贝构造函数" << std::endl;
        this->p = new int(*other.p);  // 假设p是指针类型的成员变量
    }

    MyClass(MyClass&& other) {
        std::cout << "调用移动构造函数" << std::endl;
        this->p = other.p;
        other.p = nullptr;
    }

    MyClass& operator=(const MyClass& other) {
        std::cout << "调用拷贝赋值运算符" << std::endl;
        if (this == &other) {
            return *this;
        }
        delete this->p;
        this->p = new int(*other.p);
        return *this;
    }

    MyClass& operator=(MyClass&& other) {
        std::cout << "调用移动赋值运算符" << std::endl;
        delete this->p;
        this->p = other.p;
        other.p = nullptr;
        return *this;
    }

    ~MyClass() {
        std::cout << "调用析构函数" << std::endl;
        delete this->p;
    }

private:
    int* p;
};

int main() {
    MyClass obj1;  // 调用默认构造函数
    MyClass obj2(obj1);  // 调用拷贝构造函数
    MyClass obj3(std::move(obj1));  // 调用移动构造函数
    MyClass obj4;
    obj4 = obj2;  // 调用拷贝赋值运算符
    obj4 = std::move(obj2);  // 调用移动赋值运算符
    return 0;
}

输出结果为:

调用默认构造函数
调用拷贝构造函数
调用移动构造函数
调用默认构造函数
调用拷贝赋值运算符
调用移动赋值运算符
调用析构函数
调用析构函数
调用析构函数
调用析构函数

其中,obj1的值被移动到obj3中,obj2的值被拷贝到obj4中,各个对象的特殊成员函数被自动调用。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:一起聊聊C++中的特殊成员函数 - Python技术站

(1)
上一篇 2023年6月8日
下一篇 2023年6月6日

相关文章

  • WinForm中DefWndProc、WndProc与IMessageFilter的区别

    WinForm是Windows Forms的缩写,是基于Windows的用户界面框架,提供了一个可视化的设计工具。在WinForm中,程序的窗口消息都是通过消息循环和窗口过程来处理的。其中DefWndProc、WndProc和IMessageFilter都是处理窗口消息的重要概念。接下来我将针对这三个概念进行详细讲解: DefWndProc DefWndPr…

    C# 2023年6月7日
    00
  • c# dynamic的使用详解

    下面是关于“c#dynamic的使用详解”的完整攻略,包含两个示例。 1. dynamic关键字简介 dynamic是C#中的一个关键字,它可以用于声明动态类型。使用dynamic类型可以在运行时动态地确定变量的类型,而不是在编译时确定。这使得C#可以与动态语言(如Python和JavaScript)进行交互,并且可以更容易地处理COM对象和反射。 2. d…

    C# 2023年5月15日
    00
  • C#使用Newtonsoft.Json中的JObject对象

    当我们需要在C#应用程序中处理JSON数据时,可以使用Newtonsoft.Json库。该库提供了许多用于处理JSON数据的类和方法,其中一个非常有用的类是JObject。 JObject类表示一个JSON对象,它的属性通常是一个或多个JToken对象。以下是使用JObject操作JSON数据的完整攻略。 步骤1:导入Newtonsoft.Json库 首先,…

    C# 2023年5月31日
    00
  • C# lambda表达式原理定义及实例详解

    C# lambda表达式原理定义及实例详解 1. 什么是lambda表达式 Lambda表达式是一种能够把代码作为一个参数传递的匿名函数。它是从LISP借鉴过来的一个概念,相当于是在代码里面定义一个函数,然后直接把这个函数作为一个参数传递给另一个函数,简化了代码的书写。在C#中,Lambda表达式是Func<>或Action<> 或 …

    C# 2023年6月7日
    00
  • 详解WPF中的对象资源

    下面就详细讲解一下WPF中的对象资源的使用攻略。 局部对象资源 WPF中的局部对象资源是指在某个特定元素的范围内定义的资源,只有在该元素及其子元素中才能够访问到。局部对象资源可以使用x:Key属性进行引用。 下面是一个局部对象资源的示例: <Window x:Class="WpfApp1.MainWindow" xmlns=&quo…

    C# 2023年6月1日
    00
  • C#预定义的基础类型转换

    C#是一种类型安全的语言,对于类型转换,也需要符合类型安全的规则。C#预定义了许多基础类型转换规则,这些规则可以在不同的数据类型之间进行转换,如下所示: 类型 描述 bool 布尔型 byte 无符号8位整数 char Unicode 16位字符 decimal 高精度小数 double 双精度浮点数 float 单精度浮点数 int 有符号32位整数 lo…

    C# 2023年6月7日
    00
  • .netcore 写快递100的快递物流信息查询接口的实现

    .NET Core实现快递100的快递物流信息查询接口 快递100是一个广受欢迎的快递物流信息查询网站,它提供了全国范围内的快递物流信息查询服务。本攻略将详细介绍如何使用.NET Core实现快递100的快递物流信息查询接口,并提供两个示例说明。 快递100 API 快递100提供了一组API,可以用于查询快递物流信息。其中,最常用的API是查询快递物流信息…

    C# 2023年5月17日
    00
  • C#使用命名管道Pipe进行进程通信实例详解

    下面我会给出一份完整的“C#使用命名管道Pipe进行进程通信实例详解”的攻略。 1. 什么是命名管道 命名管道(Named Pipe)是一种进程间通信(IPC)的方式,通过以名称命名的管道进行数据交换,起到了进程之间传输数据的作用。 命名管道通过文件系统创建,并且在创建的时候必须指定管道的名称。该名称由一些不包含任何非法字符的字符组成(例如,反斜杠 “\”)…

    C# 2023年6月1日
    00
合作推广
合作推广
分享本页
返回顶部