0

0

C++指定初始化 成员变量选择性初始化

P粉602998670

P粉602998670

发布时间:2025-08-27 08:49:01

|

1042人浏览过

|

来源于php中文网

原创

C++20指定初始化器允许按成员名初始化聚合类型,提升代码可读性和维护性,解决传统初始化顺序依赖、可读性差及API演进困难等问题,支持选择性初始化,未显式初始化成员将默认初始化,但仅适用于无用户声明构造函数、无虚函数等的聚合类型,且指定顺序需与声明顺序一致,不可混用位置初始化,需C++20编译器支持。

c++指定初始化 成员变量选择性初始化

C++20引入的指定初始化器(Designated Initializers)无疑是语言在提升代码可读性和初始化灵活性方面迈出的重要一步。它允许我们在初始化聚合类型(如结构体或数组)时,明确指出要赋值给哪个成员,从而实现成员变量的“选择性初始化”,极大改善了传统初始化方式中因顺序依赖和可读性差带来的种种不便。这使得代码意图更加清晰,也为未来结构体成员的增删提供了更好的兼容性。

解决方案

在C++中,尤其是C++20及更高版本,指定初始化器为我们提供了一种声明式的方式来初始化聚合类型(Aggregate Types)的成员。聚合类型通常指那些没有用户声明的构造函数、没有私有或保护的非静态数据成员、没有虚函数或虚基类的结构体或类。

使用指定初始化器时,我们通过

.member_name = value
的语法,直接为特定成员赋值。这种方式的强大之处在于,它打破了传统位置初始化对成员声明顺序的严格依赖,允许我们只初始化部分成员,而未被显式初始化的成员将执行默认初始化(对于内置类型是零初始化,对于类类型会调用其默认构造函数)。

例如,考虑一个表示配置项的结构体:

立即学习C++免费学习笔记(深入)”;

struct Config {
    int id;
    std::string name;
    bool enabled;
    double version;
};

在C++20之前,如果你只想初始化

id
enabled
,你可能需要这样:

// 传统方式,需要知道所有成员的顺序,且可能需要为不关心的成员提供默认值
Config c1 = {1, "default_name", true, 1.0}; // 假设默认值
// 或者先构造再赋值,但这不是初始化
Config c2;
c2.id = 1;
c2.enabled = true;

而使用指定初始化器,你可以这样:

// C++20 指定初始化器
Config c3 { .id = 1, .enabled = true };
// c3.name 会被默认构造为 ""
// c3.version 会被零初始化为 0.0

这种方式不仅代码更简洁,意图也更加明确。它让我们可以“跳过”那些我们不关心或希望使用默认值的成员,只专注于需要定制的那些。这在处理大型配置结构体、事件数据包或任何具有大量可选字段的结构体时,其价值尤为突出。

C++20指定初始化器解决了哪些痛点?

在我看来,指定初始化器不仅仅是一个语法糖,它切实解决了我们在日常C++开发中遇到的一些让人头疼的问题,尤其是在处理结构体或类成员较多的场景下。

首先,可读性差是传统位置初始化的一大痛点。当一个结构体有七八个甚至更多成员时,

MyStruct s = {1, "foo", true, 3.14, ..., false};
这样的代码,你需要反复对照结构体定义才能确定每个值对应哪个成员。一旦某个成员的类型或顺序发生变化,这种“盲猜”的风险就更高了。指定初始化器通过
.member = value
的方式,直接将意图写在代码中,大幅提升了代码的可读性和自文档性,你一眼就能看出哪个值赋给了哪个成员。

其次,它缓解了顺序依赖和API演进的困难。传统初始化必须严格按照成员的声明顺序来提供值。这意味着如果结构体定义中成员的顺序调整了,或者新增了一个成员,所有使用该结构体进行位置初始化的代码都可能需要修改,甚至导致编译错误或运行时逻辑错误。这对于维护大型代码库来说,无疑是一个巨大的负担。指定初始化器则允许我们只关心我们想要初始化的成员,即使结构体的成员顺序或数量发生变化,只要我们初始化的那个成员名不变,我们的初始化代码通常就不需要改动,大大增强了API的健壮性和向前兼容性。

最后,它在一定程度上避免了冗余初始化或不必要的默认构造。对于一些资源密集型或创建成本较高的成员,如果我们在结构体创建时并不需要立即初始化它们,或者希望它们保持默认状态(例如,一个

std::vector
希望开始时为空),传统方式可能需要我们显式提供一个空值或默认值。指定初始化器允许我们直接跳过这些成员,让它们执行默认初始化,这对于某些性能敏感的场景,或者仅仅是为了代码简洁,都是一个很实用的优化。

在实际项目中,如何高效利用指定初始化器进行成员变量的“选择性初始化”?

在实际开发中,指定初始化器能发挥作用的场景非常广泛,关键在于识别那些“聚合类型”和“选择性初始化”的需求。

天意易趣网拍卖系统
天意易趣网拍卖系统

前台主要功能:首选服务 注销登陆 查看使用帮助 修改添加登陆帐号拍卖商品管理 管理拍卖商品 推荐拍卖商品 删除特定拍卖 已经结束商品 拍卖分类管理 新闻管理 添加文章 删除修改 栏目管理 新闻CSS设定 新闻JS生成 初始化新闻 参数设置 用户管理 未审核用户管理 普通用户管理 高级用户管理 黄金用户管理 管理所有用户 数据库管理 压缩数据库 备份数据库 恢复数据库 批量处理 系统指标测试V1.

下载

一个非常典型的应用场景是配置结构体(Configuration Structs)。我们经常会定义一个结构体来承载各种配置参数,其中很多参数都有合理的默认值,只有少数需要根据特定环境或用户输入进行定制。例如,一个日志配置结构体:

struct LogConfig {
    std::string filePath = "/var/log/app.log";
    int logLevel = 2; // 0: Error, 1: Warn, 2: Info, 3: Debug
    bool enableConsoleOutput = true;
    size_t maxFileSizeMB = 100;
};

// 传统方式,可能需要写一长串默认值,或者先构造再赋值
// LogConfig lc1 = {"", 0, false, 0}; // 难以阅读,且容易出错

// 使用指定初始化器,只修改需要定制的项
LogConfig customLog {
    .filePath = "/tmp/my_app.log",
    .logLevel = 3, // Debug
    .maxFileSizeMB = 500
};
// customLog.enableConsoleOutput 依然是 true

这种方式让配置代码变得异常清晰,维护者一眼就能看出哪些是默认值,哪些是定制的。

另一个有用的场景是事件数据包或消息结构体。在消息队列或网络通信中,一个消息结构体可能包含多种字段,但对于特定类型的事件,只有部分字段是相关的。指定初始化器可以让我们只填充那些有意义的字段,而无需关心其他字段的默认值。

此外,结合C++11引入的默认成员初始化(Default Member Initializers),指定初始化器能提供更强大的控制。你可以为结构体成员提供一个合理的默认值,然后在需要时,通过指定初始化器来覆盖这个默认值。这使得结构体在大多数情况下都能有一个“开箱即用”的状态,同时又提供了足够的灵活性来定制。

struct UserSettings {
    bool enableNotifications = true;
    int themeId = 1; // Default theme
    std::string language = "en-US";
};

UserSettings defaultSettings; // 所有成员都是默认值
UserSettings customSettings { .themeId = 5, .language = "zh-CN" }; // 仅定制部分
// customSettings.enableNotifications 依然是 true

这种组合方式,在我看来,是现代C++中管理复杂数据结构初始化的一种非常优雅且高效的策略。

指定初始化器有哪些使用限制和潜在的“坑”?

尽管指定初始化器非常实用,但它并非没有限制,在使用时我们需要注意一些规则和潜在的“陷阱”。

最核心的一点是,指定初始化器只能用于聚合类型(Aggregate Types)。这意味着如果你的类有用户声明的构造函数(哪怕是默认构造函数)、私有或保护的非静态数据成员、虚函数或虚基类,它就不是聚合类型,也就无法使用指定初始化器。这是C++语言设计上的一条重要界限,也是很多人初次尝试时会遇到的“坑”。比如,如果你给

Config
结构体加了一个自定义构造函数,它就不再是聚合类型了:

struct NonAggregateConfig {
    int id;
    std::string name;
    NonAggregateConfig() : id(0), name("default") {} // 用户声明的构造函数
};
// NonAggregateConfig nac { .id = 1 }; // 编译错误!

其次,初始化顺序仍然重要。虽然指定初始化器允许你“跳过”成员,但你提供的指定初始化器必须按照成员在结构体中声明的顺序出现。你不能先初始化

memberB
再初始化
memberA
,如果
memberA
memberB
之前声明。例如:

struct OrderTest {
    int a;
    int b;
};
OrderTest ot1 { .a = 1, .b = 2 }; // OK
// OrderTest ot2 { .b = 2, .a = 1 }; // 编译错误!必须按声明顺序

这可能与C语言的指定初始化器行为有所不同,C语言在这方面更为宽松。在C++中,这个限制旨在保持初始化逻辑的清晰性,并避免一些潜在的歧义。

再者,不能混用指定初始化器和位置初始化器。一旦你开始使用

.member = value
的语法,你就必须对所有显式初始化的成员都使用这种语法。你不能一部分用指定初始化,一部分用位置初始化。例如,
MyStruct s = {.a = 1, 2};
是不允许的。

还有,未指定成员的默认行为需要特别注意。对于未被显式指定的成员,它们会执行默认初始化。对于内置类型(如

int
,
double
),这意味着它们会被零初始化。对于类类型(如
std::string
),这意味着会调用其默认构造函数。这通常是我们期望的行为,但在某些情况下,如果你期望它们保持未初始化状态(这在C++中通常不是一个好实践),或者期望一个特定的非零默认值,你就需要额外留意。

最后,作为C++20的特性,编译器支持是前提。如果你在使用较旧的编译器,或者目标平台不支持C++20,那么这个特性就无法使用。在实际项目开始前,确认工具链的支持情况是很有必要的。这些限制和“坑”并非不可逾越,但理解它们能帮助我们更稳健、更高效地运用指定初始化器。

相关专题

更多
C语言变量命名
C语言变量命名

c语言变量名规则是:1、变量名以英文字母开头;2、变量名中的字母是区分大小写的;3、变量名不能是关键字;4、变量名中不能包含空格、标点符号和类型说明符。php中文网还提供c语言变量的相关下载、相关课程等内容,供大家免费下载使用。

379

2023.06.20

c语言入门自学零基础
c语言入门自学零基础

C语言是当代人学习及生活中的必备基础知识,应用十分广泛,本专题为大家c语言入门自学零基础的相关文章,以及相关课程,感兴趣的朋友千万不要错过了。

608

2023.07.25

c语言运算符的优先级顺序
c语言运算符的优先级顺序

c语言运算符的优先级顺序是括号运算符 > 一元运算符 > 算术运算符 > 移位运算符 > 关系运算符 > 位运算符 > 逻辑运算符 > 赋值运算符 > 逗号运算符。本专题为大家提供c语言运算符相关的各种文章、以及下载和课程。

348

2023.08.02

c语言数据结构
c语言数据结构

数据结构是指将数据按照一定的方式组织和存储的方法。它是计算机科学中的重要概念,用来描述和解决实际问题中的数据组织和处理问题。数据结构可以分为线性结构和非线性结构。线性结构包括数组、链表、堆栈和队列等,而非线性结构包括树和图等。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

255

2023.08.09

c语言random函数用法
c语言random函数用法

c语言random函数用法:1、random.random,随机生成(0,1)之间的浮点数;2、random.randint,随机生成在范围之内的整数,两个参数分别表示上限和下限;3、random.randrange,在指定范围内,按指定基数递增的集合中获得一个随机数;4、random.choice,从序列中随机抽选一个数;5、random.shuffle,随机排序。

583

2023.09.05

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

519

2023.09.20

c语言get函数的用法
c语言get函数的用法

get函数是一个用于从输入流中获取字符的函数。可以从键盘、文件或其他输入设备中读取字符,并将其存储在指定的变量中。本文介绍了get函数的用法以及一些相关的注意事项。希望这篇文章能够帮助你更好地理解和使用get函数 。

631

2023.09.20

c数组初始化的方法
c数组初始化的方法

c语言数组初始化的方法有直接赋值法、不完全初始化法、省略数组长度法和二维数组初始化法。详细介绍:1、直接赋值法,这种方法可以直接将数组的值进行初始化;2、不完全初始化法,。这种方法可以在一定程度上节省内存空间;3、省略数组长度法,这种方法可以让编译器自动计算数组的长度;4、二维数组初始化法等等。

595

2023.09.22

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

7

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 5.7万人学习

C 教程
C 教程

共75课时 | 3.8万人学习

C++教程
C++教程

共115课时 | 10.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号