当前位置: 首页 > news >正文

[C++] 默认构造函数、参数化构造函数、拷贝构造函数、移动构造函数及其使用案例

什么是构造函数?

C++构造函数是一种特殊的成员函数,用于创建和初始化类的对象它在对象被创建时自动调用,并负责对对象的成员变量进行初始化。构造函数的名称与类的名称相同,它没有返回类型,包括默认构造函数、参数化构造函数、拷贝构造函数和移动构造函数等形式。

默认构造函数是没有参数的构造函数,在创建对象时如果没有显式地指定构造函数,则会自动调用默认构造函数。

参数化构造函数包含了一个或多个参数,用于初始化对象的成员变量。

拷贝构造函数用于创建一个新对象,并将其初始化为已有对象的副本。

构造函数可以用来执行一些初始化操作,例如为成员变量分配内存,将成员变量设置为特定的值,或者执行其他必要的初始化工作。构造函数也可以被重载,即一个类可以有多个不同参数的构造函数,以便创建不同的对象。

移动构造函数是C++11引入的一种特殊的构造函数,用于在对象被移动时,将资源从一个对象转移到另一个对象,而不是进行复制操作。

移动构造函数通常用于提高代码的性能,在处理大型对象或动态分配的内存时特别有用。通过移动构造函数,可以避免不必要的内存拷贝和分配,减少对象之间的数据传输。

移动构造函数的语法与拷贝构造函数类似,但是参数列表前有一个特殊的右值引用(&&)修饰符,表示传递的对象可以被移动。 

构造函数的使用可以提高代码的可读性和维护性,确保对象在创建时具有合适的初始状态。

构造函数使用案例

以下是关于默认构造函数、参数化构造函数、拷贝构造函数和移动构造函数的一些示例:

默认构造函数

默认构造函数是一个无参构造函数,用于创建对象时不需要传递任何参数。例如:

class MyClass {
public:// 默认构造函数MyClass() {// 初始化对象的成员变量}
};// 使用默认构造函数创建对象
MyClass obj;

参数化构造函数

参数化构造函数接收一个或多个参数,用于初始化对象的成员变量。例如:

class MyClass {
public:// 参数化构造函数MyClass(int value) {// 初始化对象的成员变量}
};// 使用参数化构造函数创建对象
MyClass obj(10);

拷贝构造函数

拷贝构造函数用于创建一个对象,其成员变量与已有对象相同。例如:

class MyClass {
public:// 拷贝构造函数MyClass(const MyClass& other) {// 将other对象的成员变量拷贝给当前对象}
};// 使用拷贝构造函数创建对象
MyClass obj1;
MyClass obj2(obj1);

移动构造函数

移动构造函数用于将资源从一个对象转移到另一个对象,而不是进行复制操作。例如:

class MyClass {
public:// 移动构造函数MyClass(MyClass&& other) {// 将资源从other对象转移给当前对象}
};// 使用移动构造函数创建对象
MyClass obj1;
MyClass obj2(std::move(obj1));

以上是各种构造函数的基本示例,具体实现可以根据具体需求进行修改和扩展。

std::move是C++标准库中的一个函数模板,它用于将一个对象转换为右值引用。通过使用std::move,可以显式地标记对象,以指示其资源可以被移动或转移。

std::move的定义如下:

template <typename T>
typename remove_reference<T>::type&& move(T&& arg) noexcept;

std::move接受一个参数arg,并返回一个T&&类型的右值引用。通过将参数arg转换为右值引用,std::move表明该对象的资源可以被移动或转移。

使用std::move的主要目的是在移动语义中提供一种明确的方式来标记对象,以避免不必要的拷贝。通过使用移动构造函数或移动赋值运算符,可以在移动语义中将资源从一个对象转移到另一个对象,而无需进行额外的内存分配和拷贝。

以下是std::move的使用示例:

#include <iostream>
#include <string>int main() {std::string str1 = "Hello";// 使用std::move将str1转移到str2std::string str2 = std::move(str1);// 此时str1的值未定义std::cout << "str1: " << str1 << std::endl;  // 输出: str1:// 输出str2的值std::cout << "str2: " << str2 << std::endl;  // 输出: str2: Helloreturn 0;
}

在上述示例中,我们使用std::movestr1转移到str2,并且在移动之后,str1的值变为未定义。这是因为std::movestr1标记为右值引用,使得资源可以被移动给str2,而不是进行拷贝。这样可以提高效率,并避免不必要的资源分配和拷贝。

综合案例一

以下是一个使用拷贝构造函数的案例:

#include <iostream>class MyClass {
public:int value;// 默认构造函数MyClass() {value = 0;}// 参数化构造函数MyClass(int val) {value = val;}// 拷贝构造函数MyClass(const MyClass& other) {value = other.value;}void displayValue() {std::cout << "Value: " << value << std::endl;}
};int main() {// 创建对象MyClass obj1(10);// 使用拷贝构造函数创建对象MyClass obj2(obj1);// 修改obj1的值obj1.value = 20;// 打印obj1和obj2的值obj1.displayValue();  // 输出: Value: 20obj2.displayValue();  // 输出: Value: 10return 0;
}

在上述案例中,我们首先通过参数化构造函数创建了一个对象obj1,并将其值初始化为10。然后,我们使用拷贝构造函数创建了另一个对象obj2,并将obj1的值拷贝给了obj2。接着,我们修改了obj1的值为20,但obj2的值没有改变。这是因为拷贝构造函数会将obj1的成员变量值拷贝给obj2,而不是共享同一份数据。

综合案例二

以下是一个使用移动构造函数的案例:

#include <iostream>class MyString {
public:char* data;// 默认构造函数MyString() {data = nullptr;}// 参数化构造函数MyString(const char* str) {int length = strlen(str) + 1;data = new char[length];strcpy_s(data, length, str);}// 拷贝构造函数MyString(const MyString& other) {int length = strlen(other.data) + 1;data = new char[length];strcpy_s(data, length, other.data);}// 移动构造函数MyString(MyString&& other) {data = other.data;other.data = nullptr;}~MyString() {if (data != nullptr) {delete[] data;}}void displayData() {std::cout << "Data: " << data << std::endl;}
};int main() {// 创建对象MyString str1("Hello");// 使用移动构造函数创建对象MyString str2(std::move(str1));// 打印str1和str2的数据// str1.displayData();  // str1的data资源已经被转移到str2,调用str1的displayData会引发空指针异常。 str2.displayData();  // 输出: Data: Helloreturn 0;
}

在上述案例中,我们首先通过参数化构造函数创建了一个MyString对象str1,并将其初始化为"Hello"。然后,我们使用移动构造函数将str1的资源移动给了str2。这样做既避免了资源的多次拷贝,又防止了删除str1时重复删除资源。在移动后,str1data指针被设置为nullptr,而str2则拥有了移动前的资源。

相关文章:

  • 图表背后的智慧:办公场景中的数据可视化革新
  • 【C语言】指针初阶2.0版本
  • vue如何使用冻结对象提升代码效率及其原理解析
  • Linux中systemv共享内存
  • 亿道信息新品EM-T195轻薄型工业平板,隆重登场!
  • Linux下检查端口占用
  • 3.1log | 62.不同路径,63. 不同路径 II,343. 整数拆分,96.不同的二叉搜索树
  • 【考研数学】零基础备考全年计划
  • 单调栈的理解
  • Facebook的元宇宙实践:数字化社交的新前景
  • 4.1.CVAT——目标检测的标注详细步骤
  • 【HbuilderX】 uniapp实现 android申请权限 和 退出app返回桌面
  • 加密与安全_探索口令加密算法(PBE)
  • 【应用多元统计分析】--数据矩阵及R语言表示
  • Flink CDC 3.0 Starrocks建表失败会导致任务卡主!
  • 【node学习】协程
  • ➹使用webpack配置多页面应用(MPA)
  • CSS中外联样式表代表的含义
  • iOS筛选菜单、分段选择器、导航栏、悬浮窗、转场动画、启动视频等源码
  • JavaScript设计模式与开发实践系列之策略模式
  • JSONP原理
  • Laravel 中的一个后期静态绑定
  • LeetCode29.两数相除 JavaScript
  • Spring技术内幕笔记(2):Spring MVC 与 Web
  • SwizzleMethod 黑魔法
  • ubuntu 下nginx安装 并支持https协议
  • Unix命令
  • vue总结
  • 阿里云应用高可用服务公测发布
  • 第三十一到第三十三天:我是精明的小卖家(一)
  • ------- 计算机网络基础
  • 老板让我十分钟上手nx-admin
  • 力扣(LeetCode)22
  • 实习面试笔记
  • 微信端页面使用-webkit-box和绝对定位时,元素上移的问题
  • Java数据解析之JSON
  • 策略 : 一文教你成为人工智能(AI)领域专家
  • $emit传递多个参数_PPC和MIPS指令集下二进制代码中函数参数个数的识别方法
  • (arch)linux 转换文件编码格式
  • (C语言)输入自定义个数的整数,打印出最大值和最小值
  • (java)关于Thread的挂起和恢复
  • (MATLAB)第五章-矩阵运算
  • (第27天)Oracle 数据泵转换分区表
  • (附源码)springboot 校园学生兼职系统 毕业设计 742122
  • (附源码)ssm教材管理系统 毕业设计 011229
  • (论文阅读30/100)Convolutional Pose Machines
  • (篇九)MySQL常用内置函数
  • (原创)攻击方式学习之(4) - 拒绝服务(DOS/DDOS/DRDOS)
  • (原創) 如何安裝Linux版本的Quartus II? (SOC) (Quartus II) (Linux) (RedHat) (VirtualBox)
  • (转)重识new
  • .“空心村”成因分析及解决对策122344
  • .babyk勒索病毒解析:恶意更新如何威胁您的数据安全
  • .NET Framework 服务实现监控可观测性最佳实践
  • .net经典笔试题
  • .net使用excel的cells对象没有value方法——学习.net的Excel工作表问题