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

当我们定义一个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日

相关文章

  • WinRAR压缩软件如何设置优先级 WinRAR设置优先级教程

    WinRAR压缩软件如何设置优先级 该攻略将详细讲解如何在WinRAR压缩软件中设置优先级。设置优先级可以调整压缩任务在计算机资源分配中的优先级,以提高压缩速度或减少对系统性能的影响。 步骤一:打开WinRAR设置 首先,需要打开WinRAR软件并进入设置界面。可以通过以下两种方法进入设置界面:1. 通过WinRAR的菜单栏:打开WinRAR,点击顶部菜单栏…

    other 2023年6月28日
    00
  • git彻底删除或变更子模块

    以下是Git彻底删除或变更子模块的完整攻略: 删除子模块 要彻底删除Git仓库中的子模块,需要执行以下步骤: 删除子模块的相关文件。在父仓库中,打开.gitmodules文件,找到要删除的子模块的条目,将其删除。然后,删除.git/modules/子模块名称目录中的所有文件。 删除子模块的引用。在父仓库中,使用以下命令删除子模块的引用: bash git r…

    other 2023年5月9日
    00
  • Java 爬虫数据异步加载如何解决

    Java爬虫在处理数据时,如果遇到异步加载的情况,可能会导致数据获取不完整或者获取失败的问题。下面我将详细讲解Java爬虫如何解决异步加载数据的问题。 1. 了解网页异步加载的原理 网页异步加载是指在页面加载完成之后,通过JavaScript等技术异步向服务器请求数据,来达到实时更新页面内容的效果。这种异步加载的方式可以大大提高用户体验,但对于爬虫的数据获取…

    other 2023年6月25日
    00
  • JVM内存分配及String常用方法解析

    当然!下面是关于\”JVM内存分配及String常用方法解析\”的完整攻略: JVM内存分配及String常用方法解析 JVM内存分配 在Java中,JVM会自动管理内存分配。以下是JVM中常见的内存区域: 堆(Heap):用于存储对象实例和数组。堆内存由垃圾回收器自动管理,对象的创建和销毁都在堆中进行。 栈(Stack):用于存储局部变量和方法调用。栈内存…

    other 2023年8月19日
    00
  • sql语句关联查询

    SQL语句关联查询 在进行数据查询操作时,经常会涉及到多张表之间的关联。SQL中就提供了关联查询的语法,用于查询多张表中的相关数据。本文将介绍SQL语句中的关联查询,以及常见的关联查询类型及示例。 关联查询的基本语法 SQL中通过JOIN语句实现关联查询,JOIN有多种类型,包括INNER JOIN、LEFT JOIN、RIGHT JOIN、FULL OUT…

    其他 2023年3月28日
    00
  • iOS13.4正式版怎么升级 iOS13.4正式版更新内容及升降级方法

    iOS 13.4正式版升级攻略 iOS 13.4正式版是苹果公司最新发布的操作系统版本,带来了一些新功能和改进。本攻略将详细介绍如何升级到iOS 13.4正式版,并提供升降级方法。 升级步骤 备份数据:在升级之前,建议您备份设备上的所有重要数据。您可以使用iCloud或iTunes进行备份。 检查设备兼容性:确保您的设备支持iOS 13.4正式版。iOS 1…

    other 2023年8月3日
    00
  • QT中出现“无法解析的外部符号”错误

    QT中出现“无法解析的外部符号”错误 在使用QT进行开发时,可能会遇到一些错误,其中”无法解析的外部符号”是比较常见的错误之一。这种错误通常会在编译或链接过程中出现,导致程序无法正常工作。在本文中,我们将深入探讨该错误的原因和解决方法。 原因 QT中的“无法解析的外部符号”错误通常是由于以下原因之一导致的: 忘记 include 头文件 当使用某个类或函数时…

    其他 2023年3月28日
    00
  • 辐射4应用程序启动异常0xc000007b错误的解决方法

    标准的markdown格式文本 在本篇文章中,我将详细介绍如何解决“辐射4应用程序启动异常0xc000007b错误”的问题,同时也会提供两条示例说明,方便大家更好地理解。 问题分析 首先,0xc000007b错误是Windows系统特有的错误码,表示“应用程序无法启动,可能是因为系统相关的依赖文件未能正确加载或者缺失”。而辐射4应用程序启动异常,可能有以下原…

    other 2023年6月25日
    00
合作推广
合作推广
分享本页
返回顶部