C++类中的六大默认成员函数详解

yizhihongxing

当我们定义一个C++类的时候,编译器会默认为我们生成六个成员函数,分别是默认构造函数、析构函数、拷贝构造函数、拷贝赋值操作符、移动构造函数和移动赋值操作符。这些成员函数可以帮助我们管理内存和类对象的创建、销毁、拷贝和赋值等操作,同时也会影响到对象的生命周期和程序的效率。因此,我们需要深入了解这六个函数的作用和实现机制,才能写出高效、健壮的代码。

默认构造函数

默认构造函数是一个没有参数的构造函数,它会被自动调用来创建一个类的对象。如果我们没有手动定义一个构造函数,编译器会自动生成一个默认构造函数。它的主要作用是初始化类的成员变量,为它们分配内存空间和设置默认值。默认构造函数的定义方法如下:

class ClassName {
public:
  ClassName();  //默认构造函数
  //其他成员函数和变量
};

当我们创建一个没有参数的类对象时,编译器就会自动调用默认构造函数。例如:

ClassName obj; //调用默认构造函数

在实际开发中,我们可以根据需要重载默认构造函数,在其中执行初始化代码,完成更多的自定义操作。例如:

class Person {
public:
  Person() : name("Unknown"), age(0) {}
  Person(string n, int a) : name(n), age(a) {}
  //其他代码和成员变量

private:
  string name;
  int age;
};

这里我们定义了一个Person类,它有两个构造函数,一个是默认构造函数,一个是带参数的构造函数。在默认构造函数中,我们把姓名初始化为"Unknown",年龄初始化为0。

析构函数

析构函数是一个没有参数的函数,它会在对象生命期结束时自动调用,用来释放对象在堆栈或者堆中分配的资源。如果我们没有手动定义一个析构函数,编译器也会自动生成一个默认析构函数。析构函数的定义方法如下:

class ClassName {
public:
  ~ClassName(); //析构函数
  //其他成员函数和变量
};

例如,在Person类的析构函数中,我们可以释放对象占用的内存空间,如下所示:

class Person {
public:
  ~Person() {
    delete[] name;
  }

  //其他代码和成员变量
private:
  char *name;
};

在这个例子中,我们使用了new运算符为字符指针name分配了一块内存空间,要在对象被销毁时释放它。因此,我们在析构函数中使用了delete[]运算符来释放内存。

拷贝构造函数

拷贝构造函数是一种特殊的构造函数,它是用来创建一个对象的副本的。当我们使用一个对象来初始化另一个对象时,拷贝构造函数就会被调用。例如:

ClassName obj1;  //调用默认构造函数
ClassName obj2 = obj1;  //调用拷贝构造函数
ClassName obj3(obj1);  //调用拷贝构造函数

拷贝构造函数的定义方法如下:

class ClassName {
public:
  ClassName(const ClassName & other);  //拷贝构造函数
  //其他成员函数和变量
};

我们需要在拷贝构造函数中复制参数对象的成员变量,并为新对象分配新的内存空间,以避免两个对象之间的相互干扰。例如:

class String {
public:
  String() : data(nullptr), len(0) {}
  String(const char* str) {
    len = strlen(str);
    if (len > 0) {
      data = new char[len + 1];
      strncpy(data, str, len + 1);
    } else {
      data = nullptr;
      len = 0;
    }
  }
  String(const String& other) {
    len = other.len;
    if (len > 0) {
      data = new char[len + 1];
      strncpy(data, other.data, len + 1);
    } else {
      data = nullptr;
      len = 0;
    }
  }

  ~String() {delete[] data;}

  //其他代码和成员变量

private:
  char* data;
  size_t len;
};

在这个例子中,我们定义了一个String类,它有三个成员函数:默认构造函数、带参数的构造函数和拷贝构造函数。我们在拷贝构造函数中使用了new运算符为新的对象分配了内存空间,并调用了strncpy函数来复制原字符串到新的内存空间中,以保证两个对象之间的内存地址不会冲突。

拷贝赋值操作符

拷贝赋值操作符是用来将一个对象赋值给另一个对象的。例如:

ClassName obj1, obj2;  //调用默认构造函数
obj2 = obj1;  //调用拷贝赋值操作符

拷贝赋值操作符的定义方法如下:

class ClassName {
public:
  ClassName& operator=(const ClassName & other);  //拷贝赋值操作符
  //其他成员函数和变量
};

在拷贝赋值操作符中,我们需要检查被赋值对象和参数对象是否相同,如果不同,则需要释放原来的内存空间,为被赋值对象重新分配内存空间,并将参数对象的成员变量复制到被赋值对象中。例如:

class String {
public:
  String() : data(nullptr), len(0) {}
  String(const char* str) {
    len = strlen(str);
    if (len > 0) {
      data = new char[len + 1];
      strncpy(data, str, len + 1);
    } else {
      data = nullptr;
      len = 0;
    }
  }
  String(const String& other) {
    len = other.len;
    if (len > 0) {
      data = new char[len + 1];
      strncpy(data, other.data, len + 1);
    } else {
      data = nullptr;
      len = 0;
    }
  }

  String& operator=(const String& other) {
    if (this != &other) {
      if (len > 0) delete[] data;
      len = other.len;
      if (len > 0) {
        data = new char[len + 1];
        strncpy(data, other.data, len + 1);
      } else {
        data = nullptr;
        len = 0;
      }
    }
    return *this;
  }

  ~String() {delete[] data;}

  //其他代码和成员变量

private:
  char* data;
  size_t len;
};

在这个例子中,我们定义了一个String类,它有两个拷贝构造函数和一个拷贝赋值操作符。在拷贝赋值操作符中,我们首先检查被赋值对象和参数对象是否相同,如果相同则不进行操作,如果不同则释放原来的内存空间,为被赋值对象重新分配内存空间,并将参数对象的成员变量复制到被赋值对象中。

移动构造函数和操作符

移动构造函数和操作符是C++11引入的两个新的成员函数,它们是为了提高程序的效率而生的。当我们使用一个右值对象初始化一个新的对象或者使用一个右值对象赋值给另一个对象时,移动构造函数和移动操作符就会被调用。例如:

ClassName obj1;  //调用默认构造函数
ClassName obj2 = std::move(obj1);  //调用移动构造函数
ClassName obj3;
obj3 = std::move(obj1);  //调用移动赋值操作符

移动构造函数和操作符的定义方法如下:

class ClassName {
public:
  ClassName(ClassName&& other);  //移动构造函数
  ClassName& operator=(ClassName&& other);  //移动赋值操作符
  //其他成员函数和变量
};

在移动构造函数和操作符中,我们需要将右值对象的内存地址转移给新的对象,然后将右值的成员变量赋值为默认值或者空指针。这样可以避免不必要的内存复制和赋值操作,提高程序的效率。例如:

class String {
public:
  String() : data(nullptr), len(0) {}
  String(const char* str) {
    len = strlen(str);
    if (len > 0) {
      data = new char[len + 1];
      strncpy(data, str, len + 1);
    } else {
      data = nullptr;
      len = 0;
    }
  }
  String(const String& other) {
    len = other.len;
    if (len > 0) {
      data = new char[len + 1];
      strncpy(data, other.data, len + 1);
    } else {
      data = nullptr;
      len = 0;
    }
  }

  String(String&& other) noexcept {
    data = other.data;
    len = other.len;
    other.data = nullptr;
    other.len = 0;
  }

  String& operator=(String&& other) noexcept {
    if (this != &other) {
      if (data) delete[] data;
      data = other.data;
      len = other.len;
      other.data = nullptr;
      other.len = 0;
    }
    return *this;
  }

  ~String() {delete[] data;}

  //其他代码和成员变量

private:
  char* data;
  size_t len;
};

在这个例子中,我们定义了一个String类,它有两个拷贝构造函数和两个移动构造函数和操作符。在移动构造函数和操作符中,我们首先将右值对象的内存地址转移给新的对象,并将右值的成员变量赋值为默认值或者空指针。这样可以避免不必要的内存复制和赋值操作,提高程序的效率。

以上就是C++中的六大默认成员函数的详解。掌握它们的作用和实现机制,可以帮助我们编写高效、健壮的程序。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++类中的六大默认成员函数详解 - Python技术站

(0)
上一篇 2023年6月26日
下一篇 2023年6月26日

相关文章

  • conceptdrift(概念漂移)

    Concept Drift (概念漂移) 什么是Concept Drift? 在机器学习和数据挖掘领域,Concept Drift (概念漂移) 是指数据的分布或者特征的分布随时间发生了变化,导致原有的模型失去了预测力。这种数据分布发生变化的情况可能来自于新的数据生成机制,也可能是由于数据收集的环境发生了变化。因为Concept Drift的存在,使得机器学…

    其他 2023年3月28日
    00
  • java实现图的邻接表存储结构的两种方式及实例应用详解

    下面就给您详细讲解“java实现图的邻接表存储结构的两种方式及实例应用详解”的完整攻略。 一、什么是图的邻接表存储结构? 图是一种重要的数据结构,主要由顶点和边组成。邻接表存储结构是一种常见的存储图的方式,它采用链表来表示图中的每个顶点及其相邻的顶点。其中,每个顶点对应一个单链表,存储该顶点与其他顶点相邻的边。 邻接表存储结构通常使用数组加链表的方式实现。数…

    other 2023年6月28日
    00
  • raid独立磁盘冗余阵列-redundantarrayofindependent…

    RAID独立磁盘冗余阵列 RAID,即独立磁盘冗余阵列(Redundant Array of Independent Disks),是一种将多个磁盘组合成一个逻辑存储单元的技术。通过RAID技术,多个硬盘可以组成一个更可靠、更安全的存储系统,从而提高数据可靠性和性能。 RAID的原理 RAID技术的核心原理是通过将多个硬盘组合在一起,形成一个逻辑盘阵列。RA…

    其他 2023年3月28日
    00
  • C++函数的嵌套调用和递归调用学习教程

    C++函数的嵌套调用和递归调用学习教程 在C++中,函数的调用是非常常见的操作。函数的嵌套调用和递归调用是函数调用中比较复杂但又常见的操作之一。本文将详细介绍这两种调用方式的概念、使用方法以及相应的示例。 函数的嵌套调用 所谓函数的嵌套调用,即在一个函数内部调用另一个函数。这种调用方式可以很好地实现代码的模块化,减少冗余代码。 函数的嵌套调用应该注意以下几点…

    other 2023年6月27日
    00
  • 使用SQL Server数据库嵌套子查询的方法

    使用SQL Server数据库嵌套子查询的方法 嵌套子查询是一种在SQL查询中使用一个查询作为另一个查询的一部分的技术。它允许我们在内部查询中使用外部查询的结果。在SQL Server中,我们可以使用嵌套子查询来解决复杂的查询需求。下面是使用SQL Server数据库嵌套子查询的方法的完整攻略。 步骤1:理解嵌套子查询的基本概念 嵌套子查询是指在一个查询中嵌…

    other 2023年7月28日
    00
  • python异步存储数据详解

    Python异步存储数据详解 什么是异步存储 异步存储指在存储数据时采用异步方式,即通过在存储数据的同时执行其他代码的方式来提高效率。相比同步存储,在存储数据时,异步存储能够更好地处理高并发、大规模数据以及对响应时间有要求的场景。 Python异步存储的实现方式 在Python中,常用的异步存储方式有以下两种: 使用协程存储 协程是一种轻量级的线程,可以在不…

    other 2023年6月27日
    00
  • 关于favicon.ico的两三事(最好就是放根目录)

    关于 favicon.ico 的两三事(最好就是放根目录),我为您准备了以下的完整攻略。 一、什么是 favicon.ico favicon.ico 是指网站的图标,可以在浏览器标签页、书签栏等位置显示。favicon.ico 文件通常被放置在网站根目录下,浏览器会自动请求并加载它。 二、为什么需要 favicon.ico 1.提高网站可识别度和品牌形象,方…

    other 2023年6月27日
    00
  • win10家庭中文版没有本地用户和组怎么办

    win10家庭中文版没有本地用户和组怎么办 在 Windows 10 家庭中文版中,由于系统限制,无法像专业版和企业版那样管理本地用户和组。但是,我们仍然可以利用以下三种方法来解决这个问题。 方法一:使用Windows 10 家庭中文版的默认用户和组 Windows 10 家庭中文版自带了一些默认用户和组,我们可以利用这些默认账户来管理系统。通过在“计算机管…

    其他 2023年3月28日
    00
合作推广
合作推广
分享本页
返回顶部