《C++ Primer》导学系列:第 13 章 - 拷贝控制
13.1 拷贝、赋值与析构函数
拷贝控制是C++中类设计的重要组成部分,用于管理对象的复制、赋值和销毁过程。理解并正确实现拷贝控制函数(拷贝构造函数、拷贝赋值运算符和析构函数)对于编写健壮和高效的C++程序至关重要。
13.1.1 拷贝构造函数
拷贝构造函数用于创建对象的副本。它的声明形式如下:
ClassName(const ClassName &other);
当对象被复制时,拷贝构造函数会被调用。例如,当对象以值传递的方式作为参数传递或从函数返回时,拷贝构造函数将被调用。
示例代码
#include <iostream>class Example {
public:Example(int v) : value(v) {}Example(const Example &other) : value(other.value) {std::cout << "Copy constructor called" << std::endl;}int getValue() const { return value; }
private:int value;
};void printExample(Example e) {std::cout << "Value: " << e.getValue() << std::endl;
}int main() {Example ex1(10);Example ex2 = ex1; // 调用拷贝构造函数printExample(ex1); // 调用拷贝构造函数return 0;
}
13.1.2 拷贝赋值运算符
拷贝赋值运算符用于将一个对象的值赋给另一个对象。它的声明形式如下:
ClassName& operator=(const ClassName &other);
拷贝赋值运算符通常用于对象已经存在的情况下进行赋值操作。
示例代码
#include <iostream>class Example {
public:Example(int v) : value(v) {}Example(const Example &other) : value(other.value) {std::cout << "Copy constructor called" << std::endl;}Example& operator=(const Example &other) {if (this != &other) {value = other.value;std::cout << "Copy assignment operator called" << std::endl;}return *this;}int getValue() const { return value; }
private:int value;
};int main() {Example ex1(10);Example ex2(20);ex2 = ex1; // 调用拷贝赋值运算符std::cout << "ex2 value: " << ex2.getValue() << std::endl;return 0;
}
13.1.3 析构函数
析构函数用于释放对象使用的资源。它的声明形式如下:
~ClassName();
析构函数在对象的生命周期结束时被自动调用,例如当对象超出作用域或被显式删除时。
示例代码
#include <iostream>class Example {
public:Example(int v) : value(v) {}~Example() {std::cout << "Destructor called" << std::endl;}int getValue() const { return value; }
private:int value;
};int main() {Example ex1(10);std::cout << "ex1 value: " << ex1.getValue() << std::endl;return 0;
}
13.1.4 三/五法则
C++中有一个重要的设计原则,称为“三/五法则”,意思是如果一个类定义了析构函数、拷贝构造函数或拷贝赋值运算符中的一个,那么它很可能需要定义所有这三个函数。随着C++11标准引入移动语义,这个原则扩展为“五法则”,包括移动构造函数和移动赋值运算符。
示例代码
#include <iostream>
#include <utility>class Example {
public:Example(int v) : value(new int(v)) {}Example(const Example &other) : value(new int(*other.value)) {std::cout << "Copy constructor called" << std::endl;}Example& operator=(const Example &other) {if (this != &other) {delete value;value = new int(*other.value);std::cout << "Copy assignment operator called" << std::endl;}return *this;}~Example() {delete value;std::cout << "Destructor called" << std::endl;}// C++11移动构造函数Example(Example &&other) noexcept : value(other.value) {other.value = nullptr;std::cout << "Move constructor called" << std::endl;}// C++11移动赋值运算符Example& operator=(Example &&other) noexcept {if (this != &other) {delete value;value = other.value;other.value = nullptr;std::cout << "Move assignment operator called" << std::endl;}return *this;}int getValue() const { return *value; }
private:int* value;
};int main() {Example ex1(10);Example ex2 = std::move(ex1); // 调用移动构造函数Example ex3(20);ex3 = std::move(ex2); // 调用移动赋值运算符return 0;
}
13.1.5 阻止拷贝
在某些情况下,我们可能希望阻止对象被拷贝或赋值。可以通过将拷贝构造函数和拷贝赋值运算符声明为delete
来实现这一点。
示例代码
class NonCopyable {
public:NonCopyable() = default;NonCopyable(const NonCopyable &) = delete;NonCopyable& operator=(const NonCopyable &) = delete;
};int main() {NonCopyable obj1;// NonCopyable obj2 = obj1; // 错误:拷贝构造函数被删除// NonCopyable obj3;// obj3 = obj1; // 错误:拷贝赋值运算符被删除return 0;
}
重点与难点分析
重点:
- 拷贝构造函数:了解何时会调用拷贝构造函数,掌握其实现方法。
- 拷贝赋值运算符:理解拷贝赋值运算符的使用场景和实现细节。
- 析构函数:熟悉析构函数的作用和调用时机。
- 三/五法则:掌握何时需要同时实现拷贝构造函数、拷贝赋值运算符和析构函数,理解移动语义下的五法则。
- 阻止拷贝:了解如何通过
delete
关键字阻止对象的拷贝和赋值。
难点:
- 自我赋值检测:在拷贝赋值运算符中处理自我赋值的情况。
- 资源管理:确保在析构函数中正确释放资源,避免内存泄漏和其他资源管理问题。
- 编写健壮的拷贝控制函数:理解并正确实现拷贝控制函数,以确保对象在复制、赋值和销毁过程中行为一致且安全。
- 实现移动语义:正确实现移动构造函数和移动赋值运算符,提高程序性能。
练习题解析
- 练习13.1:编写一个类,包含拷贝构造函数、拷贝赋值运算符和析构函数,测试它们的调用情况。
-
- 示例代码:
#include <iostream>class Example {
public:Example(int v) : value(v) {}Example(const Example &other) : value(other.value) {std::cout << "Copy constructor called" << std::endl;}Example& operator=(const Example &other) {if (this != &other) {value = other.value;std::cout << "Copy assignment operator called" << std::endl;}return *this;}~Example() {std::cout << "Destructor called" << std::endl;}int getValue() const { return value; }
private:int value;
};int main() {Example ex1(10);Example ex2 = ex1; // 调用拷贝构造函数ex2 = ex1; // 调用拷贝赋值运算符return 0;
}
- 练习13.2:修改练习13.1的类,使其包含一个指向动态内存的指针,确保拷贝控制函数正确管理该内存。
-
- 示例代码
#include <iostream>class Example {
public:
Example(int v) : value(new int(v)) {}
Example(const Example &other) : value(new int(*other.value)) {std::cout << "Copy constructor called" << std::endl;
}
Example& operator=(const Example &other) {if (this != &other) {delete value;value = new int(*other.value);std::cout << "Copy assignment operator called" << std::endl;}return *this;
}
~Example() {delete value;std::cout << "Destructor called" << std::endl;
}
int getValue() const { return *value; }
private:
int* value;
};int main() {Example ex1(10);Example ex2 = ex1; // 调用拷贝构造函数ex2 = ex1; // 调用拷贝赋值运算符std::cout << "ex2 value: " << ex2.getValue() << std::endl;return 0;
}
- 练习13.3:编写一个类,包含移动构造函数和移动赋值运算符,测试它们的调用情况。
-
- 示例代码:
#include <iostream>class Example {
public:Example(int v) : value(new int(v)) {}Example(const Example &other) : value(new int(*other.value)) {std::cout << "Copy constructor called" << std::endl;}Example& operator=(const Example &other) {if (this != &other) {delete value;value = new int(*other.value);std::cout << "Copy assignment operator called" << std::endl;}return *this;}Example(Example &&other) noexcept : value(other.value) {other.value = nullptr;std::cout << "Move constructor called" << std::endl;}Example& operator=(Example &&other) noexcept {if (this != &other) {delete value;value = other.value;other.value = nullptr;std::cout << "Move assignment operator called" << std::endl;}return *this;}~Example() {delete value;std::cout << "Destructor called" << std::endl;}int getValue() const { return *value; }
private:int* value;
};int main() {Example ex1(10);Example ex2 = std::move(ex1); // 调用移动构造函数Example ex3(20);ex3 = std::move(ex2); // 调用移动赋值运算符return 0;
}
- 练习13.4:编写一个类,阻止其对象的拷贝和赋值。
-
- 示例代码:
#include <iostream>class NonCopyable {
public:NonCopyable() = default;NonCopyable(const NonCopyable &) = delete;NonCopyable& operator=(const NonCopyable &) = delete;
};int main() {NonCopyable obj1;// NonCopyable obj2 = obj1; // 错误:拷贝构造函数被删除// NonCopyable obj3;// obj3 = obj1; // 错误:拷贝赋值运算符被删除return 0;
}
总结与提高
本节总结:
- 掌握了拷贝控制的基本概念和操作,包括拷贝构造函数、拷贝赋值运算符和析构函数。
- 理解了拷贝控制函数在对象复制、赋值和销毁过程中的作用和调用时机。
- 学会了在类中正确实现拷贝控制函数,以确保对象在不同生命周期阶段的正确行为。
- 理解了“三/五法则”的重要性,学会了在类设计中同时实现必要的拷贝控制函数和移动语义。
- 掌握了如何通过
delete
关键字阻止对象的拷贝和赋值。
提高建议:
- 多练习拷贝控制函数的实现:通过编写更多涉及拷贝控制的类,熟悉各种管理方法的用法,提高对拷贝控制的理解和实现能力。
- 深入理解资源管理的原理:通过阅读文档和相关书籍,深入理解资源管理的实现原理和使用场景,提高编写高效代码的能力。
- 优先使用智能指针:在实际项目中,尽量使用智能指针管理动态内存,以减少手动内存管理带来的错误,提高代码的可读性和可维护性。
- 优化移动语义:在类设计中,合理运用移动构造函数和移动赋值运算符,提高程序的性能和资源利用效率。
13.2 拷贝控制与资源管理
拷贝控制函数在管理资源时非常重要,特别是当类涉及动态内存分配或其他资源(如文件句柄、网络连接等)时。通过正确实现拷贝控制函数,可以确保资源在对象复制、赋值和销毁过程中得到正确的管理和释放。
13.2.1 拷贝控制与动态内存
在涉及动态内存的类中,拷贝构造函数、拷贝赋值运算符和析构函数的实现需要特别注意,以确保内存安全。
示例代码
#include <iostream>
#include <algorithm> // for std::copyclass DynamicArray {
public:DynamicArray(size_t size) : size(size), data(new int[size]()) {}DynamicArray(const DynamicArray &other) : size(other.size), data(new int[other.size]) {std::copy(other.data, other.data + other.size, data);std::cout << "Copy constructor called" << std::endl;}DynamicArray& operator=(const DynamicArray &other) {if (this != &other) {
相关文章:
《C++ Primer》导学系列:第 13 章 - 拷贝控制
13.1 拷贝、赋值与析构函数 拷贝控制是C++中类设计的重要组成部分,用于管理对象的复制、赋值和销毁过程。理解并正确实现拷贝控制函数(拷贝构造函数、拷贝赋值运算符和析构函数)对于编写健壮和高效的C++程序至关重要。 13.1.1 拷贝构造函数 拷贝构造函数用于创建对象的副…...
c++ 图论2 深度优先算法和广度优先算法
修改一下深度优先算法和广度优先算法,标出每一个节点相对于遍历起始位置的层级,遍历起始起点为第一层,和第一层相连的节点为第二层,以此类推 定义一个新的结构 struct NodeWithLevel {TreeNode* node;int level;NodeWithLevel(T…...

【Qt】初识QtQt Creator
一.简述Qt 1.什么是Qt Qt 是⼀个 跨平台的 C 图形⽤⼾界⾯应⽤程序框架 。它为应⽤程序开发者提供了建⽴艺术级图形界⾯所需的所有功能。它是完全⾯向对象的,很容易扩展。Qt 为开发者提供了⼀种基于组件的开发模式,开发者可以通过简单的拖拽和组合来实现…...

Android 11.0 修改系统显示大小导航栏消失
Android 11.0 修改系统显示大小导航栏消失 1.显示大小设置为大时,导航栏图标不显示。 设置为大,较大,最大时,导航栏图标不显示。 2.开始怀疑是导航栏被隐藏了,各种折腾无效。 3.发现: frameworks/base/pa…...

RocketMQ源码学习笔记:Producer启动流程
这是本人学习的总结,主要学习资料如下 马士兵教育rocketMq官方文档 目录 1、Overview1.1、创建MQClientInstance1.1.1、检查1.1.1、MQClientInstance的ID 1.2、MQClientInstance.start() 1、Overview 这是发送信息的代码样例, DefaultMQProducer produ…...
Node.js 和浏览器环境中都使用 WebSocket
使用WebSocket为什么不适配双端 浏览器环境本身就支持 WebSocket,直接使用 JavaScript 内置的 WebSocket 对象来建立连接。 Node中本身并没有内置 WebSocket 协议的支持,所以需要使用第三方库 ws来实现 WebSocket 功能。 一. 使用跨平台 WebSocket 库 …...

css美化滚动条样式
效果展示 实现 滚动条宽,高度 /* 整体滚动条 */ ::-webkit-scrollbar {width: 10px; }/* 滚动条轨道 */ ::-webkit-scrollbar-track {background-color: #ffffff;border-radius: 6px; }/* 滚动条滑块 */ ::-webkit-scrollbar-thumb {background-color: #888;borde…...
由浅入深,走进深度学习(补充篇:转置卷积和FCN)
本期内容是针对神经网络层结构的一个补充,主要内容是:转置卷积和全连接卷积网络 相关内容: 由浅入深,走进深度学习(2)_卷积层-CSDN博客 由浅入深,走进深度学习(补充篇:…...

Linux基础篇——目录结构
基本介绍 Linux的文件系统是采用级层式的树状目录结构,在此结构中的最上层是根目录"/",然后在根目录下再创建其他的目录 在Linux中,有一句经典的话:在Linux世界里,一切皆文件 Linux中根目录下的目录 具体的…...
星际编码:Swifter.Json,.NET宇宙中的数据处理新星
概述 在数字化的星辰大海中,数据是宇宙的通用语言。在.NET这一广袤的星系中,JSON作为信息交换的媒介,扮演着至关重要的角色。今天,我们要探索的是一颗新星——Swifter.Json,一个功能全面且性能卓越的JSON序列化和反序列…...

python 压缩数据
requests 是 Python 中一个非常流行的 HTTP 库,用于发送各种 HTTP 请求。下面是一个使用 requests 库发送简单 GET 请求和 POST 请求的示例: 首先,确保你已经安装了 requests 库。如果还没有安装,可以使用 pip 进行安装ÿ…...

nacos在k8s上的集群安装实践
目录 概述实践nfs安装使用 k8s持久化nacos安装创建角色部署数据库执行数据库初始化语句部署nacos ingress效果展示问题修复 结束 概述 本文主要对 nacos 在k8s上的集群安装 进行说明与实践。主要版本信息,k8s: 1.27.x,nacos: 2.0.3。运行环境为 centos 7…...

数据结构—判断题
1.数据的逻辑结构说明数据元素之间的顺序关系,它依赖于计算机的存储结构。 答案:错误 2.(neuDS)在顺序表中逻辑上相邻的元素,其对应的物理位置也是相邻的。 答案:正确 3.若一个栈的输入序列为{1, 2, 3, 4, 5},则不…...
树莓派挂载的移动硬盘badblocks坏道屏蔽,以这个为准
!!!use 这里要设置块大小和磁盘相同 badblocks -b 4096 -s -c 512 -v -o /a/2/bads4.txt /dev/sda5 检测完重新检测跳过之前的记录 badblocks -i /a/2/bads4.txt -b 4096 -s -c 512 -v -o /a/2/bads5.txt /dev/sda5 可以查看磁盘具体block总数和大小 sudo dumpe2fs /dev/sda5 …...

Unity开箱即用的UGUI面板的拖拽移动功能
文章目录 👉一、背景👉二、效果图👉三、原理👉四、核心代码👉五,总结 👉一、背景 之前做PC项目时常常有面板拖拽移动的需求,今天总结封装一下,做成一个随时随地可复用的…...

春秋云境:CVE-2022-25411[漏洞复现]
根据题目提示和CNNVD优先寻找后台管理地址 靶机启动后,使用AWVS进行扫描查看网站结构 在这里可以看到后台管理的登录地址:/admin/,根据题目提示可知是弱口令 尝试admin、123456、admin666、admin123、admin888...等等常见弱口令 正确的账户…...

java基础知识点全集
JAVA的所有知识点 一、基础的数组、数据类型、输入输出二、类与对象1. 三大特征(1) 封装(2)继承(3)多态 2. 类的实例化(1) 类通过NEW来创建(2) 类的继承&…...

如何完成域名解析验证
一:什么是DNS解析: DNS解析是互联网上将人类可读的域名(如www.example.com)转换为计算机可识别的IP地址(如192.0.2.1)的过程,大致遵循以下步骤: 查询本地缓存:当用户尝…...
2024年6月个人工作生活总结
title: 2024年6月个人工作生活总结 urlname: code-for-2024-06 tags: 代码积累知识总结 categories:我的程序代码 date: 2024-06-30 00:00:00 photos:gallery/tech/c2.jpg 本文为 2024年6月工作生活总结。 研发编码 编码和注释 因某些需要,重拾了2019年的工程代码…...

Json与Java类
简介 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。JSON数据由键值对构成,并以易于阅读的文本形式展现,支持数组、对象、字符串、数字、布尔值…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...

【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...

P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...

tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...

优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...

基于SpringBoot在线拍卖系统的设计和实现
摘 要 随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统,主要的模块包括管理员;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...