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

《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;
}

重点与难点分析

重点

  1. 拷贝构造函数:了解何时会调用拷贝构造函数,掌握其实现方法。
  2. 拷贝赋值运算符:理解拷贝赋值运算符的使用场景和实现细节。
  3. 析构函数:熟悉析构函数的作用和调用时机。
  4. 三/五法则:掌握何时需要同时实现拷贝构造函数、拷贝赋值运算符和析构函数,理解移动语义下的五法则。
  5. 阻止拷贝:了解如何通过delete关键字阻止对象的拷贝和赋值。

难点

  1. 自我赋值检测:在拷贝赋值运算符中处理自我赋值的情况。
  2. 资源管理:确保在析构函数中正确释放资源,避免内存泄漏和其他资源管理问题。
  3. 编写健壮的拷贝控制函数:理解并正确实现拷贝控制函数,以确保对象在复制、赋值和销毁过程中行为一致且安全。
  4. 实现移动语义:正确实现移动构造函数和移动赋值运算符,提高程序性能。

练习题解析

  1. 练习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;
}
  1. 练习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;
}
  1. 练习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;
}
  1. 练习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;
}

总结与提高

本节总结

  1. 掌握了拷贝控制的基本概念和操作,包括拷贝构造函数、拷贝赋值运算符和析构函数。
  2. 理解了拷贝控制函数在对象复制、赋值和销毁过程中的作用和调用时机。
  3. 学会了在类中正确实现拷贝控制函数,以确保对象在不同生命周期阶段的正确行为。
  4. 理解了“三/五法则”的重要性,学会了在类设计中同时实现必要的拷贝控制函数和移动语义。
  5. 掌握了如何通过delete关键字阻止对象的拷贝和赋值。

提高建议

  1. 多练习拷贝控制函数的实现:通过编写更多涉及拷贝控制的类,熟悉各种管理方法的用法,提高对拷贝控制的理解和实现能力。
  2. 深入理解资源管理的原理:通过阅读文档和相关书籍,深入理解资源管理的实现原理和使用场景,提高编写高效代码的能力。
  3. 优先使用智能指针:在实际项目中,尽量使用智能指针管理动态内存,以减少手动内存管理带来的错误,提高代码的可读性和可维护性。
  4. 优化移动语义:在类设计中,合理运用移动构造函数和移动赋值运算符,提高程序的性能和资源利用效率。

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 深度优先算法和广度优先算法

修改一下深度优先算法和广度优先算法&#xff0c;标出每一个节点相对于遍历起始位置的层级&#xff0c;遍历起始起点为第一层&#xff0c;和第一层相连的节点为第二层&#xff0c;以此类推 定义一个新的结构 struct NodeWithLevel {TreeNode* node;int level;NodeWithLevel(T…...

【Qt】初识QtQt Creator

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

Android 11.0 修改系统显示大小导航栏消失

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

RocketMQ源码学习笔记:Producer启动流程

这是本人学习的总结&#xff0c;主要学习资料如下 马士兵教育rocketMq官方文档 目录 1、Overview1.1、创建MQClientInstance1.1.1、检查1.1.1、MQClientInstance的ID 1.2、MQClientInstance.start() 1、Overview 这是发送信息的代码样例&#xff0c; DefaultMQProducer produ…...

Node.js 和浏览器环境中都使用 WebSocket

使用WebSocket为什么不适配双端 浏览器环境本身就支持 WebSocket&#xff0c;直接使用 JavaScript 内置的 WebSocket 对象来建立连接。 Node中本身并没有内置 WebSocket 协议的支持&#xff0c;所以需要使用第三方库 ws来实现 WebSocket 功能。 一. 使用跨平台 WebSocket 库 …...

css美化滚动条样式

效果展示 实现 滚动条宽&#xff0c;高度 /* 整体滚动条 */ ::-webkit-scrollbar {width: 10px; }/* 滚动条轨道 */ ::-webkit-scrollbar-track {background-color: #ffffff;border-radius: 6px; }/* 滚动条滑块 */ ::-webkit-scrollbar-thumb {background-color: #888;borde…...

由浅入深,走进深度学习(补充篇:转置卷积和FCN)

本期内容是针对神经网络层结构的一个补充&#xff0c;主要内容是&#xff1a;转置卷积和全连接卷积网络 相关内容&#xff1a; 由浅入深&#xff0c;走进深度学习&#xff08;2&#xff09;_卷积层-CSDN博客 由浅入深&#xff0c;走进深度学习&#xff08;补充篇&#xff1a…...

Linux基础篇——目录结构

基本介绍 Linux的文件系统是采用级层式的树状目录结构&#xff0c;在此结构中的最上层是根目录"/"&#xff0c;然后在根目录下再创建其他的目录 在Linux中&#xff0c;有一句经典的话&#xff1a;在Linux世界里&#xff0c;一切皆文件 Linux中根目录下的目录 具体的…...

星际编码:Swifter.Json,.NET宇宙中的数据处理新星

概述 在数字化的星辰大海中&#xff0c;数据是宇宙的通用语言。在.NET这一广袤的星系中&#xff0c;JSON作为信息交换的媒介&#xff0c;扮演着至关重要的角色。今天&#xff0c;我们要探索的是一颗新星——Swifter.Json&#xff0c;一个功能全面且性能卓越的JSON序列化和反序列…...

python 压缩数据

requests 是 Python 中一个非常流行的 HTTP 库&#xff0c;用于发送各种 HTTP 请求。下面是一个使用 requests 库发送简单 GET 请求和 POST 请求的示例&#xff1a; 首先&#xff0c;确保你已经安装了 requests 库。如果还没有安装&#xff0c;可以使用 pip 进行安装&#xff…...

nacos在k8s上的集群安装实践

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

数据结构—判断题

1.数据的逻辑结构说明数据元素之间的顺序关系&#xff0c;它依赖于计算机的存储结构。 答案&#xff1a;错误 2.(neuDS)在顺序表中逻辑上相邻的元素&#xff0c;其对应的物理位置也是相邻的。 答案&#xff1a;正确 3.若一个栈的输入序列为{1, 2, 3, 4, 5}&#xff0c;则不…...

树莓派挂载的移动硬盘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面板的拖拽移动功能

文章目录 &#x1f449;一、背景&#x1f449;二、效果图&#x1f449;三、原理&#x1f449;四、核心代码&#x1f449;五&#xff0c;总结 &#x1f449;一、背景 之前做PC项目时常常有面板拖拽移动的需求&#xff0c;今天总结封装一下&#xff0c;做成一个随时随地可复用的…...

春秋云境:CVE-2022-25411[漏洞复现]

根据题目提示和CNNVD优先寻找后台管理地址 靶机启动后&#xff0c;使用AWVS进行扫描查看网站结构 在这里可以看到后台管理的登录地址&#xff1a;/admin/&#xff0c;根据题目提示可知是弱口令 尝试admin、123456、admin666、admin123、admin888...等等常见弱口令 正确的账户…...

java基础知识点全集

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

如何完成域名解析验证

一&#xff1a;什么是DNS解析&#xff1a; DNS解析是互联网上将人类可读的域名&#xff08;如www.example.com&#xff09;转换为计算机可识别的IP地址&#xff08;如192.0.2.1&#xff09;的过程&#xff0c;大致遵循以下步骤&#xff1a; 查询本地缓存&#xff1a;当用户尝…...

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月工作生活总结。 研发编码 编码和注释 因某些需要&#xff0c;重拾了2019年的工程代码…...

Json与Java类

简介 JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;易于人阅读和编写&#xff0c;同时也易于机器解析和生成。JSON数据由键值对构成&#xff0c;并以易于阅读的文本形式展现&#xff0c;支持数组、对象、字符串、数字、布尔值…...

终极解决方案:uesave-rs 让你轻松编辑虚幻引擎游戏存档

终极解决方案&#xff1a;uesave-rs 让你轻松编辑虚幻引擎游戏存档 【免费下载链接】uesave 项目地址: https://gitcode.com/gh_mirrors/ue/uesave 还在为游戏存档损坏而抓狂吗&#xff1f;面对一堆看不懂的二进制数据&#xff0c;想要修改游戏进度却无从下手&#xff…...

白城腾讯广告服务商

在白城&#xff0c;有不少企业想借助腾讯广告拓展业务&#xff0c;这就离不开靠谱的腾讯广告服务商。今天就和大家聊聊白城腾讯广告服务商的那些事儿&#xff0c;长春中网互联技术在这一领域表现就相当不错。白城腾讯广告服务商现状行业报告显示&#xff0c;近几年白城地区对腾…...

给嵌入式新手的保姆级指南:JTAG、SWD、J-Link、ST-Link到底怎么选?

嵌入式开发调试工具全指南&#xff1a;从JTAG到SWD的实战选择策略 第一次拿到STM32开发板时&#xff0c;看着板子上那排密密麻麻的调试接口针脚&#xff0c;我盯着J-Link和ST-Link这两个名词发了半小时呆——它们到底有什么区别&#xff1f;为什么有的教程用JTAG接线&#xff0…...

3KW无线充电系统设计:开环控制与闭环控制的MATLAB Simulink仿真模型,采用双边L...

3KW无线充电系统设计&#xff08;MATLAB simulink仿真模型&#xff09; 控制方式&#xff1a;开环控制闭环控制 拓扑结构&#xff1a;双边LCC拓扑结构 输入电压&#xff1a;750V 输出电压&#xff1a;400V 传输功率&#xff1a;3KW 最近在折腾一个3KW无线充电系统的仿真项目&am…...

闽北哥-做个无用之人,方成大用

做个无用之人 ——方成大用 “太有用的人&#xff0c;一定走不远。” &#x1f33f; 人生是一场‘无心生大用’的修行。 白木香树越能结香&#xff0c;越被千疮百孔&#xff1b; 无用之树&#xff0c;反得自然生长。 &#x1f4a1; 真正的价值&#xff0c;不在“有”&#xff…...

Leetcode 数据结构刷题 ->链表1

[27. 移除元素]移除等于所给值的元素&#xff0c;我们可以直接使用双指针&#xff0c;对着来的。关键就是把不等于x的值&#xff08;我改一下&#xff0c;没用val&#xff09;&#xff0c;放到后面去&#xff0c;这样前面就全部都是不等于x值&#xff0c;再计数即可。看代码就对…...

游戏报错终极解决方案 DirectX修复工具深度解析

在Windows操作系统环境下&#xff0c;DirectX组件是游戏和多媒体软件运行的核心基础。 随着游戏产业的快速发展&#xff0c;越来越多的玩家在运行游戏时遇到了各种技术问题。 其中&#xff0c;DirectX组件缺失、损坏、报错是最为常见的问题之一&#xff0c;严重影响了用户的游戏…...

LFM2.5-1.2B-Thinking-GGUF基础教程:单页Web界面交互逻辑与后处理机制

LFM2.5-1.2B-Thinking-GGUF基础教程&#xff1a;单页Web界面交互逻辑与后处理机制 1. 模型与平台介绍 LFM2.5-1.2B-Thinking-GGUF是Liquid AI推出的轻量级文本生成模型&#xff0c;专为低资源环境优化设计。这个镜像采用内置GGUF模型文件和llama.cpp运行时&#xff0c;提供了…...

S7-200 PLC与组态王称重配料生产线自动控制系统:后继产品包含梯形图、接线图、原理图及I...

S7-200 PLC和组态王称重配料生产线自动控制系统配料 我们主要的后发送的产品有&#xff0c;带解释的梯形图接线图原理图图纸&#xff0c;io分配&#xff0c;组态画面上周刚结了个小单子&#xff0c;给本地一家饲料厂改了套半自动的称重配料线&#xff0c;用的就是S7-200 PLC加…...

Qwen3-TTS部署案例:车载中控系统离线多语种导航语音引擎集成

Qwen3-TTS部署案例&#xff1a;车载中控系统离线多语种导航语音引擎集成 在智能座舱快速演进的今天&#xff0c;车载语音交互已从“能听清”迈向“听得懂、说得好、有温度”的新阶段。传统TTS方案常受限于网络依赖、语种覆盖窄、响应延迟高、方言适配弱等问题&#xff0c;难以…...