我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C++聚合体初始化aggregateinitialization详细介绍

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C++聚合体初始化aggregateinitialization详细介绍

聚合体初始化(aggregate initialization)

C++有很多初始化对象的方法。其中之一叫做 聚合体初始化(aggregate initialization) ,这是聚合体专有的一种初始化方法。

从C语言引入的初始化方式是用花括号括起来的一组值来初始化类:

struct Data {
    std::string name;
    double value;
};
Data x = {"test1", 6.778};

自从C++11起,你可以忽略等号:

Data x{<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->"test1", 6.778};

自从C++17起,聚合体可以拥有基类。也就是说像下面这种从其他类派生出的子类也可以使用这种初始化方法:

struct MoreData : Data {
    bool done;
}
MoreData y{{"test1", 6.778}, false};

如你所见,聚合体初始化时可以用一个子聚合体初始化来初始化类中来自基类的成员。

另外,你甚至可以省略子聚合体初始化的花括号:

MoreData y{<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->"test1", 6.778, false};

这样写将遵循嵌套聚合体初始化时的通用规则,你传递的实参被用来初始化哪一个成员取决于它们的顺序。

扩展聚合体初始化的动机

如果没有这个特性,那么所有的派生类都不能使用聚合体初始化,这意味着你要像下面这样定义构造函数:

struct Cpp14Data : Data {
    bool done;
    Cpp14Data (const std::string& s, double d, bool b) : Data{s, d}, done{b} {
    }
};
Cpp14Data y{"test1", 6.778, false};

现在我们不再需要定义任何构造函数就可以做到这一点。

我们可以直接使用嵌套花括号的语法来实现初始化,

如果给出了内层初始化需要的所有值就可以省略内层的花括号:

MoreData x{{"test1", 6.778}, false};    // 自从C++17起OK
MoreData y{"test1", 6.778, false};      // OK

注意因为现在派生类也可以是聚合体,所以其他的一些初始化方法也可以使用:

MoreData u;     // OOPS:value/done未初始化
MoreData z{};   // OK: value/done初始化为0/false

如果觉得这样很危险,可以使用成员初始值:

struct Data {
    std::string name;
    double value{0.0};
};
struct Cpp14Data : Data {
    bool done{false};
};

或者,继续提供一个默认构造函数。

使用聚合体扩展

聚合体初始化的一个典型应用场景是对一个派生自C风格结构体并且添加了新成员的类进行初始化。例如:

struct Data {
    const char* name;
    double value;
};
struct CppData : Data {
    bool critical;
    void print() const {
        std::cout << '[' << name << ',' << value << "]\n";
    }
};
CppData y{{"test1", 6.778}, false};
y.print();

这里,内层花括号里的参数被传递给基类Data

注意你可以跳过初始化某些值。在这种情况下,跳过的成员将会进行默认初始化

(基础类型会被初始化为0false或者nullptr,类类型会默认构造)。

例如:

CppData x1{};           // 所有成员默认初始化为0值
CppData x2{{"msg"}}     // 和{{"msg", 0.0}, false}等价
CppData x3{{}, true};   // 和{{nullptr, 0.0}, true}等价
CppData x4;             // 成员的值未定义

注意使用空花括号和不使用花括号完全不同:

x1的定义会把所有成员默认初始化为0值,

因此字符指针name被初始化为nullptr

double类型的value初始化为0.0

bool类型的flag初始化为falsex4的定义没有初始化任何成员。所有成员的值都是未定义的。

你也可以从非聚合体派生出聚合体。例如:

struct MyString : std::string {
    void print() const {
        if (empty()) {
            std::cout << "<undefined>\n";
        }
        else {
            std::cout << c_str() << '\n';
        }
    }
};
MyString x{{"hello"}};
MyString y{"world"};

注意这不是通常的具有多态性的public继承,因为std::string没有虚成员函数,

你需要避免混淆这两种类型。

你甚至可以从多个基类和聚合体中派生出聚合体:

template<typename T>
struct D : std::string, std::complex<T>
{
    std::string data;
};

你可以像下面这样使用和初始化:

D<float> s{{"hello"}, {4.5, 6.7}, "world"}; // 自从C++17起OK
D<float> t{"hello", {4.5, 6.7}, "world"};   // 自从C++17起OK
std::cout << s.data;                        // 输出:"world"
std::cout << static_cast<std::string>(s);   // 输出:"hello"
std::cout << static_cast<std::complex<float>>(s);   //输出:(4.5,6.7)

内部嵌套的初值列表将按照继承时基类声明的顺序传递给基类。

这个新的特性也可以帮助我们用很少的代码定义重载的lambda

聚合体的定义

总的来说,在C++17中满足如下条件之一的对象被认为是 聚合体 :

  • 是一个数组
  • 或者是一个满足如下条件的 类类型 (classstructunion):
  • 没有用户定义的和explicit的构造函数
  • 没有使用using声明继承的构造函数
  • 没有privateprotected的非静态数据成员
  • 没有virtual函数
  • 没有virtual, private, protected的基类

然而,要想使用聚合体初始化来 初始化 聚合体,那么还需要满足如下额外的约束:

  • 基类中没有private或者protected的成员
  • 没有private或者protected的构造函数

下一节就有一个因为不满足这些额外约束导致编译失败的例子。

C++17引入了一个新的类型特征is_aggregate<>

来测试一个类型是否是聚合体:

template<typename T>
struct D : std::string, std::complex<T> {
    std::string data;
};
D<float> s{{"hello"}, {4.5, 6.7}, "world"};         // 自从C++17起OK
std::cout << std::is_aggregate<decltype(s)>::value; // 输出1(true)

向后的不兼容性

注意下面的例子不能再通过编译:

struct Derived;
struct Base {
    friend struct Derived;
private:
    Base() {
    }
};
struct Derived : Base {
};
int main()
{
    Derived d1{};   // 自从C++17起ERROR
    Derived d2;     // 仍然OK(但可能不会初始化)
}

在C++17之前,Derived不是聚合体。因此

Derived d1{};

会调用Derived隐式定义的默认构造函数,这个构造函数会调用基类Base的构造函数。

尽管基类的默认构造函数是private的,但在派生类的构造函数里调用它也是有效的,

因为派生类被声明为友元类。

自从C++17起,例子中的Derived是一个聚合体,所以它没有隐式的默认构造函数

(构造函数没有使用using声明继承)。因此,d1的初始化将是一个聚合体初始化,

如下表达式:

std::is_aggregate<Derived>::value

将返回true

然而,因为基类有一个private的构造函数(见上一节)所以不能使用花括号来初始化。

这和派生类是否是基类的友元无关。

到此这篇关于C++聚合体初始化aggregate initialization详细介绍的文章就介绍到这了,更多相关C++聚合体初始化内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C++聚合体初始化aggregateinitialization详细介绍

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C++聚合体初始化aggregateinitialization详细介绍

这篇文章主要介绍了C++聚合体初始化aggregateinitialization,C++有很多初始化对象的方法。其中之一叫做聚合体初始化(aggregateinitialization),这是聚合体专有的一种初始化方法
2023-02-03

C++聚合体初始化的方法是什么

本篇内容介绍了“C++聚合体初始化的方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!聚合体初始化(aggregate initial
2023-07-05

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录