前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >聚合体及其初始化———指派初始化器,C++长大了,你应该知道指派初始化器

聚合体及其初始化———指派初始化器,C++长大了,你应该知道指派初始化器

作者头像
程序员的园
发布于 2024-07-18 05:16:56
发布于 2024-07-18 05:16:56
22000
代码可运行
举报
运行总次数:0
代码可运行

如果知道我会死在哪里,那我将永远不去那个地方 -查理 芒格

前言

初始化列表作为C++11引入的特性,不仅用于初始化变量,还可以用于初始化结构体,如

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct Point
{
  int x;
  int y;
};
void using_initial_list()
{
  //初始化变量
  auto channel_num{ 10 };
  
  //初始化聚合体
  Point p{ 10, 30 };
  std::vector<int> vec{ 1,2 };//contains two elements, 1 and 2;
}

其中结构体Point就是聚合体,那么聚合体是什么呢,

聚合体

聚合体定义

聚合体是一种数据类型,其可以是:

  1. 可以是数组
  2. 可以是具有如下特性的类类型(含class、struct、union),
    • 没有用户声明、用户提供、explicit的构造函数(情形1)
    • 没有继承的构造函数(情形2)
    • 所有成员都是公有的(没有私有/受保护的非静态数据成员)(情形3)
    • 没有虚函数(情形4)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//情形1
class People
{
public:
std::string m_name;
int m_age;
//People(std::string name, int age):m_name(name),m_age(age){}//非聚合体
//People(){}//非聚合体
};

//情形2
class Base {
public:
  Base(int i, int j){};
  int i =10;
  int j =30;
};

class People:public Base//非聚合体
{
public:
std::string m_name;
int m_age;
};


//情形3:
class People
{
public:
std::string m_name;
int m_age;
private:
 int math_score;//非聚合体
};

//情形4
class People
{
public:
std::string m_name;
int m_age;
virtual void worker(){};//非聚合体
};

网上部分资料认为聚合体不能有自定义的析构函数,但是在MSVC2022 C++20的编译环境下,具有自定义析构函数的仍被认为是聚合体

聚合体元素

聚合体中的元素为:

  1. 数组:数组中所有的元素
  2. 类类型(类、结构体、联合体):所有不是匿名位域非静态数据成员
  3. 类类型(类、结构体、联合体):所有直接基类的和自己的,所有不是匿名位域和匿名联合体非静态数据成员

聚合体初始化

上文提到,可以直接使用初始化列表对聚合体进行初始化,C++20新增指派初始化器对聚合体进行初始化。指派初始化器是初始化列表的扩展,通过指定聚合体中的元素进行初始化

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct AudioInfo
{
    int channel_num;
    int channel_layout;
    int sample_rate;
    int frame_cout;
    int bit_depth;
};
AudioInfo info1{1,1,48000,1024,16};//初始化列表
AudioInfo info2{ .channel_num = 1, .channel_layout {1}, .bit_depth=16,  };//指派初始化器

聚合体初始化操作

常见错误

  1. 初始化器列表中的初始式化子句多于聚合体中的元素个数
  2. 以空初始化器列表({})初始化边界未知的数组。
  3. 如果聚合体是union且显示初始化元素不止一个(即联合体只能初始化一个元素)
  4. 指派初始化器初始化元素时涉及到窄化转换则出现编译错误。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
char cv[4] = {'a', 's', 'd', 'f', 0}; // 错误,多余元素个数
int x[] = {};                         // 错误,空的初始化器列表初始化未知边界的数组

union u { int a; const char* b; };
 
u a = {1};                   // OK:显式初始化成员 a
u b = {0, "asdf"};           // 错误:显式初始化两个成员
u c = {"asdf"};              // 错误:按元素声明顺序初始化,即不能以 "asdf" 初始化 int
 
// C++20 指派初始化器列表
u d = {.b = "asdf"};         // OK:可以显式初始化非开头元素
u e = {.a = 1, .b = "asdf"}; // 错误:显式初始化两个成员

u f{.a{10.0}};//错误,窄化转换

显示初始化元素规则

  1. 使用指派初始化器时必须指明该类中的一个非静态成员,并且显示初始化元素必须是成员或成员的元素,不可指向基类的成员
  2. 如果该元素是匿名联合体成员,使用指派初始化器列表初始化时可以直接使用联合体成员的指派初始化器进行初始化。(example 1 )
  3. 当初始化器以嵌套的形式初始化对象,初始化器内的数据以及初始化器子句均对应某个元素的初始化,且对应顺序为聚合体内元素的声明顺序,初始化时可以隐式转换,但是不能窄化转换。(example 2)
  4. 如果初始化列表不为空,则初始化前n个元素,按照声明的顺序的前n个或下标元素的前n个
  5. 如果初始化列表为空,则不存在显示初始化的元素。
  6. 如果聚合体非联合体,且存在元素没有显示初始化,则(1)如果聚合体为元素指定默认值,则使用默认值初始化,(2)如果元素不是引用,则使用默认值初始化,(3)程序非良构,编译报错;
  7. 如果聚合体为联合体,如果未显示初始化时,则(1)联合体内可变成员具有默认值,则该成员以默认值初始化。(2)首个元素使用以{}进行复制初始化。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//example 1
struct S
{
  union 
  {
  int a;
  int b;
  };
  int c;
};
S s{.a{10}, .c{30}};

//example 2
struct A
{
    int x;
 
    struct B
    {
        int b1;
        int b2;
    } b;
};
 a = {1, {2, 3}}; // 以 1 初始化 a.x,以 2 初始化 a.b.b1,以 3 初始化 a.b.b2
 a = {1, {2, 3.0}}; // error, 存在窄化转换

指派初始化器

以上其实已涉及导指派初始化器,但作为C++20新特性,着重介绍用法及特性如下:

  1. 每个指派符必须为聚合体内的非静态数据成员,且指派的顺序需与聚合体内元素的声明顺序相同(example 221)
  2. 指派初始化器指定聚合体内每个元素的初始化值,当出现窄化转换会报错(example 222)
  3. 只能为联合体指定一个初始化器,无论联合体作为聚合体抑或联合体作为聚合体内元素时,均只能为其指定一个初始化器(example 223)
  4. 对于非联合体的聚合体未提供指派初始化器的元素,或初始化器子句的数量少于聚合体元素数量时,则如果提供了默认值则用默认值初始化,否则使用{}进行初始化。(example 224)
  5. 如果聚合体具有一个匿名联合体成员,那么对应的指派初始化器必须指明联合体内的一个成员。(example 223)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//example 221
struct AudioInfo
{
    int channel_num;
    int channel_layout;
    int sample_rate;
    int frame_cout;
    int bit_depth;
};

AudioInfo info{ .channel_num = 1, .channel_layout {1}, .bit_depth=16,  };//sample_rate和frame_cout初始化为0
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//example 222
struct AudioInfo
{
    int channel_num;
    int channel_layout;
    int sample_rate;
    int frame_cout;
    int bit_depth;
};//example 222
struct AudioInfo
{
    int channel_num;
    int channel_layout;
    int sample_rate;
    int frame_cout;
    int bit_depth;
};

AudioInfo info{ .channel_num = 1, .channel_layout {1}, .bit_depth=16.0,  };//error,double类型的16.0需要转换为int型,报错
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//example 223
struct S
{
  union 
  {
    int a;
    int b;
  };
  int c;
};

S s{.a{10}, .c{30}};


union u { int a; const char* b; };
 
u f = {.b = "asdf"};         // OK:联合体的活跃成员是 b
u g = {.a = 1, .b = "asdf"}; // 错误:只可提供一个初始化器  
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//example 224
struct C
{
    string str;
    int a = 2;
    int b= -1;
};
 
A{.b = 21}  //使用{}初始化str, 使用2初始化a,使用21初始化b
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-02-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员的园 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档