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

C++ 构造函数最佳实践

文章目录

      • 1. 构造函数应该做什么
        • 1.1 初始化成员变量
        • 1.2 分配资源
        • 1.3 遵循 RAII 原则
        • 1.4 处理异常情况
      • 2. 构造函数不应该做什么
        • 2.1 避免做大量的工作
        • 2.2 不要在构造函数中调用虚函数
        • 2.3 避免在构造函数中执行复杂的初始化逻辑
        • 2.4 避免调用可能抛出异常的代码
      • 3. 构造函数的其他最佳实践
        • 3.1 使用`explicit`防止隐式转换
        • 3.2 尽量避免在构造函数中使用`new`
        • 3.3 考虑使用委托构造函数
      • 4. 总结
        • 构造函数应该做:
        • 构造函数不应该做:

C++ 中,构造函数是类的初始化方法,构造的主要目的是为对象分配资源并设置初始状态。在设计和使用构造函数时,最佳实践可以使得代码更健壮、清晰且高效。

本篇文章 即 C++ 构造函数最佳实践。

1. 构造函数应该做什么

1.1 初始化成员变量

构造函数的首要职责是确保对象的所有成员变量都被正确初始化。C++ 提供了构造函数初始化列表,应该优先使用这种方式来初始化成员,而不是在构造函数体中赋值。这是因为初始化列表可以直接调用成员的构造函数,而赋值则会先调用默认构造函数,然后进行赋值,可能会导致额外的性能开销。

示例:

class MyClass {
private:int x;std::string name;
public:// 使用初始化列表进行成员初始化MyClass(int a, const std::string& n) : x(a), name(n) {}
};
1.2 分配资源

构造函数的另一个主要职责是为对象动态分配资源,如动态内存、文件句柄等。在分配资源时,确保使用适当的资源管理机制(如智能指针)来防止资源泄漏。

示例:

class MyClass {
private:std::unique_ptr<int> data;
public:MyClass(int value) : data(std::make_unique<int>(value)) {}  // 使用智能指针管理资源
};
1.3 遵循 RAII 原则

C++ 的资源管理通常基于RAII(Resource Acquisition Is Initialization,资源获取即初始化)原则。构造函数应该负责资源的获取,而析构函数则负责释放资源。通过遵循这一原则,构造函数能够确保对象在其生命周期内持有有效的资源。

示例:

class FileHandler {
private:std::fstream file;
public:FileHandler(const std::string& filename) : file(filename) {if (!file.is_open()) {throw std::runtime_error("Failed to open file");}}~FileHandler() {file.close();  // 析构函数负责资源释放}
};
1.4 处理异常情况

如果构造函数中可能会遇到无法继续初始化的情况(如分配资源失败),应该在构造函数中抛出异常,确保对象不会处于部分初始化状态。一般情况下,构造函数要么成功创建一个有效的对象,要么抛出异常,表明对象创建失败。

示例:

class MyClass {
public:MyClass(int value) {if (value < 0) {throw std::invalid_argument("Negative value is not allowed");}}
};

2. 构造函数不应该做什么

2.1 避免做大量的工作

构造函数的职责是初始化对象,而不是执行复杂的逻辑。构造函数不应该执行大量计算、网络调用、文件操作等繁重任务。如果需要做这些工作,应该将它们放在单独的初始化函数或惰性加载机制中,以避免构造函数的复杂化。

错误示例:

class MyClass {
public:MyClass() {// 错误:构造函数中进行大量计算for (int i = 0; i < 1000000; ++i) {// 复杂计算}}
};

改进:

class MyClass {
public:MyClass() {// 构造函数尽量简单}void initData() {for (int i = 0; i < 1000000; ++i) {// 将复杂逻辑移出构造函数}}
};
2.2 不要在构造函数中调用虚函数

在构造函数中调用虚函数会导致意外行为,因为在构造函数执行期间,派生类的部分还没有被构造,虚函数的多态性机制还没有完全生效。因此,在构造函数中调用虚函数时,实际上调用的是当前类的版本,而不是派生类中的重写版本。

对象构造遵循从基类到派生类的顺序:
①首先调用基类的构造函数,完成基类部分的初始化。
②然后调用派生类的构造函数,完成派生类部分的初始化。

派生类还没有构造完成,先构造了基类,虚函数指针指向基类函数地址,调用执行后调用的就是基类的函数了。
错误示例:

#include <iostream>class Base {
public:Base() {std::cout << "Base constructor called" << std::endl;print();  // 在构造函数中调用虚函数}virtual void print() const {std::cout << "Base::print" << std::endl;}
};class Derived : public Base {
private:int value;
public:Derived(int v) : value(v) {std::cout << "Derived constructor called" << std::endl;}virtual void print() const override {std::cout << "Derived::print, value = " << value << std::endl;}
};int main() {Derived d(10);return 0;
}

编译运行:

[jn@jn build]$ ./a.out
Base constructor called
Base::print
Derived constructor called
[jn@jn build]$ 

改进:

  • 在构造函数中避免使用虚函数。如果需要在对象创建后调用某些逻辑,考虑将其放在专门的初始化函数中或工厂函数中。
2.3 避免在构造函数中执行复杂的初始化逻辑

构造函数应该尽量保持简洁,不应该执行过多的复杂逻辑或初始化。复杂的初始化可以通过专门的初始化函数来实现,特别是在需要初始化多个资源或对象的情况下。

错误示例:

class MyClass {
public:MyClass() {// 错误:构造函数中执行复杂初始化逻辑initializeResources();setupConnections();}private:void initializeResources() {// 复杂资源初始化}void setupConnections() {// 网络或数据库连接初始化}
};

改进:

class MyClass {
public:MyClass() {// 构造函数尽量简单}void initialize() {initializeResources();setupConnections();}private:void initializeResources() {// 复杂资源初始化}void setupConnections() {// 网络或数据库连接初始化}
};
2.4 避免调用可能抛出异常的代码

在构造函数中,如果发生异常,可能会导致对象处于部分初始化状态。虽然 C++ 会在构造函数抛出异常时自动调用析构函数来清理已分配的资源,但避免在构造函数中进行可能抛出异常的操作仍是一个好的实践。如果确实需要处理异常,最好将复杂逻辑放在其他成员函数中。

错误示例:

class MyClass {
public:MyClass() {// 错误:构造函数中调用可能抛出异常的操作performRiskyOperation();}private:void performRiskyOperation() {throw std::runtime_error("Operation failed");}
};

改进:

class MyClass {
public:MyClass() {// 构造函数保持简单}void initialize() {try {performRiskyOperation();} catch (const std::exception& e) {std::cerr << "Error: " << e.what() << std::endl;}}private:void performRiskyOperation() {throw std::runtime_error("Operation failed");}
};

3. 构造函数的其他最佳实践

3.1 使用explicit防止隐式转换

构造函数在某些情况下可能会被用作隐式转换函数,导致意外的行为。为了避免这种情况,使用 explicit 关键字显式禁止构造函数的隐式转换。

示例:

class MyClass {
public:explicit MyClass(int value) {// 禁止隐式转换}
};MyClass obj = 10;  // 错误:隐式转换被 explicit 禁止
3.2 尽量避免在构造函数中使用new

尽量避免在构造函数中直接使用 new 分配动态内存,而是使用智能指针(如 std::unique_ptrstd::shared_ptr)来管理资源,避免内存泄漏。

错误示例:

class MyClass {
private:int* data;
public:MyClass() {data = new int[100];  // 错误:直接使用 new 分配内存}~MyClass() {delete[] data;}
};

改进:

class MyClass {
private:std::unique_ptr<int[]> data;  // 使用智能指针管理内存
public:MyClass() : data(std::make_unique<int[]>(100)) {}
};
3.3 考虑使用委托构造函数

C++11 引入了委托构造函数的概念,可以减少代码重复,避免在多个构造函数中进行相同的初始化操作。

示例:

class MyClass {
private:int x;std::string name;
public:MyClass(int a) : MyClass(a, "Default") {}  // 委托给另一个构造函数MyClass(int a, const std::string& n) : x(a), name(n) {}
};

4. 总结

构造函数应该做:
  1. 初始化成员变量,尤其是通过初始化列表(注意初始化顺序问题)。
  2. 分配资源,如动态内存、文件句柄等,并遵循 RAII 原则。
  3. 处理异常情况,在无法初始化时抛出异常,避免部分初始化对象。
构造函数不应该做:
  1. 不要做大量工作,如复杂的计算或 I/O 操作。
  2. 不要调用虚函数,因为在构造期间虚函数的多态性机制尚未生效。
  3. 避免复杂的初始化逻辑,这些逻辑可以放在单独的函数中。
  4. 避免调用可能抛出异常的代码,并确保异常得到适当处理。

相关文章:

C++ 构造函数最佳实践

文章目录 1. 构造函数应该做什么1.1 初始化成员变量1.2 分配资源1.3 遵循 RAII 原则1.4 处理异常情况 2. 构造函数不应该做什么2.1 避免做大量的工作2.2 不要在构造函数中调用虚函数2.3 避免在构造函数中执行复杂的初始化逻辑2.4 避免调用可能抛出异常的代码 3. 构造函数的其他…...

C++——关联式容器(4):set和map

在接触了诸如二叉搜索树、AVL树、红黑树的树形结构之后&#xff0c;我们对树的结构有了大致的了解&#xff0c;现在引入真正的关联式容器。 首先&#xff0c;先明确了关联式容器的概念。我们之前所接触到的如vector、list等容器&#xff0c;我们知道他们实际上都是线性的数据结…...

Spring Mybatis 基本使用 总结

1. 简介 Mybatis库可以简化数据库的操作&#xff0c;专注于sql语句。 2.搭建步骤 2.1 在pom.xml引入mybatis <dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.11</version> </dep…...

接口幂等性和并发安全的区别?

目录标题 幂等性并发安全总结 接口幂等性和并发安全是两个不同的概念&#xff0c;虽然它们在设计API时都很重要&#xff0c;但侧重点不同。 幂等性 定义&#xff1a;幂等性指的是无论对接口进行多少次相同的操作&#xff0c;结果都是一致的。例如&#xff0c;HTTP的PUT和DELE…...

【记录一下VMware上开虚拟端口映射到公网】

材料 win11 和装在vmware上的ubuntu 步骤一在Ubuntu上配置静态地址&#xff0c;配置如下 vim /etc/netplan/01-network-manager-all.yaml(此文件看系统上对应的是哪个文件&#xff0c;建议先备份)network:version: 2renderer: NetworkManagerethernets:ens33:dhcp4: falseadd…...

半导体器件制造5G智能工厂数字孪生物联平台,推进制造业数字化转型

半导体器件制造行业作为高科技领域的核心驱动力&#xff0c;正积极探索和实践以5G智能工厂数字孪生平台为核心的新型制造模式。这一创新不仅极大地提升了生产效率与质量&#xff0c;更为制造业的未来发展绘制了一幅智能化、网络化的宏伟蓝图。 在半导体器件制造5G智能工厂中&a…...

数据结构之存储位置

p 和 "hello,world"存储在内存哪个区域&#xff1f;( ) (鲁科安全) int main() { char *p "hello,world"; return 0; } p是栈区&#xff0c;”hello,world”是.ro段 一个由C/C编译的程序&#xff0c;会将占用的内存分为几个部分&#xff1a;堆、栈、代…...

传输层协议(TCP和UDP)

目录 一、UDP 1、UDPAPI 2、UDPAPI的使用 二、TCP 1、TCPAPI 2、TCP的相关特性 2.1 确认应答 2.2 超时重传 2.3 连接管理&#xff08;三次握手&#xff0c;四次挥手&#xff09; 2.4 滑动窗口 2.5 流量控制 2.6 拥塞控制 2.7 延时应答 2.8 捎带应答 2.9 面向字节…...

智能仓库|基于springBoot的智能无人仓库管理设计与实现(附项目源码+论文+数据库)

私信或留言即免费送开题报告和任务书&#xff08;可指定任意题目&#xff09; 目录 一、摘要 二、相关技术 三、系统设计 四、数据库设计 五、核心代码 六、论文参考 七、源码获取 一、摘要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xf…...

2.《DevOps》系列K8S部署CICD流水线之部署NFS网络存储与K8S创建StorageClass

架构 服务器IP服务名称硬件配置192.168.1.100k8s-master8核、16G、120G192.168.1.101k8s-node18核、16G、120G192.168.1.102k8s-node28核、16G、120G192.168.1.103nfs2核、4G、500G操作系统:Rocky9.3 后续通过K8S部署GitLab、Harbor、Jenkins 一、环境准备 #关闭防火墙开机自…...

【数据仓库】数据仓库常见的数据模型——维度模型

文章部分图参考自&#xff1a;多维数据模型各种类型&#xff08;星型、雪花、星座、交叉连接&#xff09; - 知乎 (zhihu.com) 文章部分文字canla一篇文章搞懂数据仓库&#xff1a;四种常见数据模型&#xff08;维度模型、范式模型等&#xff09;-腾讯云开发者社区-腾讯云 (ten…...

【Kubernetes】常见面试题汇总(三十)

目录 82. Worker 节点宕机&#xff0c;简述 Pods 驱逐流程。 特别说明&#xff1a; 题目 1-68 属于【Kubernetes】的常规概念题&#xff0c;即 “ 汇总&#xff08;一&#xff09;~&#xff08;二十二&#xff09;” 。 题目 69-113 属于【Kubernetes】的生产应用题。 8…...

【Web】PolarCTF2024秋季个人挑战赛wp

EZ_Host 一眼丁真命令注入 payload: ?host127.0.0.1;catf* 序列一下 exp: <?phpclass Polar{public $lt;public $b; } $pnew Polar(); $p->lt"system"; $p->b"tac /f*"; echo serialize($p);payload: xO:5:"Polar":2:{s:2:"…...

职业技能大赛-自动化测试笔记分享-2

一、时间等待处理 1、强制等待(无条件等待) 使用方法:time.sleep(delay) delay的单位为秒,delay设置多少秒页面就会等待多长时间,容易让线程挂掉,使程序抛异常,所以要慎用此方法。 #导入强制等待模块 import time from selenium import webdriverwd = webdriver.Chro…...

LeetCode讲解篇之1343. 大小为 K 且平均值大于等于阈值的子数组数目

文章目录 题目描述题解思路题解代码 题目描述 题解思路 题目让我们求长度为k的子数组并且该子数组的平均值大于threshold&#xff0c;对于这题&#xff0c;我们可以考虑维护一个长度为k的窗口&#xff0c;窗口不断向右滑动&#xff0c;遍历所有长度为k的子数组&#xff0c;我们…...

电子元件制造5G智能工厂物联数字孪生平台,推进制造业数字化转型

5G智能工厂与物联数字孪生平台的融合应用&#xff0c;不仅为电容器制造业注入了新的活力&#xff0c;更为整个制造业的数字化转型树立了新的标杆。电子元件制造过程中&#xff0c;数字孪生平台通过实时监测生产线的各个环节&#xff0c;实现了生产流程的可视化监控。管理人员可…...

【成品论文】2024年华为杯研赛E题25页高质量成品论文(后续会更新

您的点赞收藏是我继续更新的最大动力&#xff01; 一定要点击如下的卡片链接&#xff0c;那是获取资料的入口&#xff01; 点击链接加入【2024华为杯研赛资料汇总】&#xff1a;https://qm.qq.com/q/Mxv2XNWxUc https://qm.qq.com/q/Mxv2XNWxUc 高速公路应急车道紧急启用模型…...

【后端】【语言】【python】python常见操作

文章目录 1. List 操作2. JSON 操作3. Dict 操作 下面是分别演示 list、json、dict 操作 1. List 操作 my_list[] # List 操作示例 my_list [1, 2, 3, "apple", True]# 添加元素 my_list.append("new item") # [1, 2, 3, "apple", True, &qu…...

二叉树的链式结构和递归程序的递归流程图

二叉树的链式存储结构是指&#xff0c;用链表来表示一棵二叉树&#xff0c;即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成&#xff0c;数据域和左右指针域&#xff0c;左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。链式结构又分…...

研究生如何利用 ChatGPT 帮助开展日常科研工作?

ChatGPT科研 一、 如何精读论文“三步提问法”1.为什么要做这个研究&#xff1f;这个研究是否值得我们做&#xff1f;2.他们怎么做这个研究3.他们发现了什么&#xff1f; 二、如何利用ChatGPT快速精读论文&#xff1f;首先&#xff0c;“三步走之第一步”--为什么要做这个研究&…...

面试官是算法出身,感觉没有问的很难?揭秘AI大模型面试高频题及应对策略!

面试官是算法出身&#xff0c;感觉没有问的很难第一个AI Agent系统是多Agent系统还是单Agent系统&#xff1f;Think-Execute循环机制的prompt工程设计是你自己写的吗&#xff1f;能简单说一下Think-Executor的prompt是怎么设计的吗&#xff1f;系统用的基座模型是什么&#xff…...

Wandb账号串线了?手把手教你排查和修复‘实验记录跑到别人账户’的坑

Wandb账号串线排查指南&#xff1a;如何避免实验记录跑到他人账户 实验室的GPU服务器指示灯闪烁着&#xff0c;你刚提交的模型训练任务在终端显示"Run completed successfully"&#xff0c;但刷新了十几次Wandb面板——那些期待中的损失曲线和评估指标依然不见踪影。…...

Flux.1文生图大模型本地部署实战:从零到一的ComfyUI配置指南

1. 为什么选择Flux.1和ComfyUI组合 如果你正在寻找一个既强大又灵活的AI绘画解决方案&#xff0c;Flux.1和ComfyUI的组合绝对值得考虑。Flux.1作为目前最大的开源文本到图像模型之一&#xff0c;拥有12B参数的强大生成能力&#xff0c;而ComfyUI则以其模块化的工作流设计著称&a…...

开发者利器:OpenClaw调用nanobot自动生成Python单元测试

开发者利器&#xff1a;OpenClaw调用nanobot自动生成Python单元测试 1. 为什么需要AI生成单元测试&#xff1f; 作为一名长期奋战在一线的开发者&#xff0c;我深知单元测试的重要性&#xff0c;但同时也饱受编写测试用例的折磨。每次面对一个新函数&#xff0c;我需要&#…...

Vue3-DateTime-Picker:如何让Vue 3应用的时间选择变得简单又优雅?

Vue3-DateTime-Picker&#xff1a;如何让Vue 3应用的时间选择变得简单又优雅&#xff1f; 【免费下载链接】vue3-date-time-picker Datepicker component for Vue 3 项目地址: https://gitcode.com/gh_mirrors/vu/vue3-date-time-picker 你是否曾经在开发Vue应用时&…...

Qwen2.5-VL-7B-Instruct保姆级:SSH远程部署+ngrok内网穿透共享演示

Qwen2.5-VL-7B-Instruct保姆级&#xff1a;SSH远程部署ngrok内网穿透共享演示 想不想在远程服务器上部署一个能“看图说话”的AI助手&#xff0c;还能随时随地通过网页访问它&#xff1f;今天&#xff0c;我就带你手把手搞定这件事。 我们将一起完成两个核心任务&#xff1a;…...

超图神经网络(HGNN)实战:从多模态数据构建到节点特征提取

超图神经网络实战&#xff1a;多模态数据融合与节点特征提取全流程解析 在电商推荐系统中&#xff0c;我们常常需要同时处理用户行为日志、商品图像和评论文本——这三种异构数据如何统一建模&#xff1f;传统图神经网络&#xff08;GNN&#xff09;在处理这类多模态关联时往往…...

OpenClaw+GLM-4.7-Flash:3步实现自动化邮件处理

OpenClawGLM-4.7-Flash&#xff1a;3步实现自动化邮件处理 1. 为什么需要自动化邮件处理&#xff1f; 每天早晨打开邮箱&#xff0c;看到堆积如山的未读邮件时&#xff0c;那种窒息感我太熟悉了。作为技术团队的接口人&#xff0c;我的邮箱常年保持着200未读邮件的状态——有…...

干货合集:AI论文网站深度测评与推荐2026最新版

2026年真正好用的AI论文网站&#xff0c;核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测&#xff0c;千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队&#xff0c;覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…...

保姆级避坑指南:在openEuler 22.03 LTS上用kubeadm 1.32.2部署K8s集群(含阿里云镜像加速)

保姆级避坑指南&#xff1a;在openEuler 22.03 LTS上用kubeadm 1.32.2部署K8s集群&#xff08;含阿里云镜像加速&#xff09; 如果你正在openEuler系统上尝试部署Kubernetes集群&#xff0c;很可能已经踩过不少坑。不同于常见的CentOS或Ubuntu&#xff0c;openEuler作为国产操作…...