前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >【c++11】列表初始化与声明

【c++11】列表初始化与声明

作者头像
用户11029103
发布2025-01-19 12:17:31
发布2025-01-19 12:17:31
13800
代码可运行
举报
文章被收录于专栏:技术学习技术学习
运行总次数:0
代码可运行

🔥个人主页Quitecoder

🔥专栏c++笔记仓

相比于C++98/03,C++11则带来了数量可观的变化,其中包含了约140个新特性,以及对C++03标准中约600个缺陷的修正,这使得C++11更像是从C++98/03中孕育出的一种新语言。相比较而言,C++11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更强大,而且能提升程序员的开发效率,公司实际项目开发中也用得比较多

1.列表初始化

在C++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定:

代码语言:javascript
代码运行次数:0
复制
struct Point
{
	int _x;
	int _y;
};
int main()
{
	int array1[] = { 1, 2, 3, 4, 5 };
	int array2[5] = { 0 };
	Point p = { 1, 2 };
	return 0;
}

C++11扩大了用大括号括起的列表的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用列表初始化时,可添加等号(=),也可不添加

代码语言:javascript
代码运行次数:0
复制
struct Point
{
	int _x;
	int _y;
};
int main()
{
	int x1 = 1;
	int x2{ 2 };
	int array1[]{ 1, 2, 3, 4, 5 };
	int array2[5]{ 0 };
	Point p{ 1, 2 };
	// C++11中列表初始化也可以适用于new表达式中
	int* pa = new int[4] { 0 };
	return 0;
}

创建对象时也可以使用列表初始化方式调用构造函数初始化

代码语言:javascript
代码运行次数:0
复制
class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{
		cout << "Date(int year, int month, int day)" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2025, 1, 1); // old style
	// C++11支持的列表初始化,这里会调用构造函数初始化
	Date d2{ 2025, 1, 2 };
	Date d3 = { 2025, 1, 3 };
	return 0;
}
std::initializer_list

std::initializer_list 是 C++11 引入的一个标准库类模板,用于支持统一的列表初始化语法,并简化初始化列表的处理。它常用于用户自定义的类或函数,使其可以接受固定数量的初始化元素。

1. 定义与特点

std::initializer_list 的定义位于头文件 <initializer_list> 中。它的主要特点包括:

  • 表示一组常量值的不可变数组(只读的顺序容器)。
  • 提供对数组元素的访问,但不能修改其中的值
  • 由编译器隐式生成,用户无需直接构造 initializer_list 对象。

2. 主要功能

std::initializer_list 提供以下功能:

  • 支持范围循环遍历
  • 提供成员函数 begin()end(),便于访问底层元素。
  • 提供成员函数 size() 获取元素个数。

3. 使用示例

(1)用于函数参数

一个函数可以接受 std::initializer_list 参数,从而支持传入多个值作为初始化列表:

代码语言:javascript
代码运行次数:0
复制
#include <initializer_list>
#include <iostream>

void print(std::initializer_list<int> values) {
    for (int value : values) {
        std::cout << value << " ";
    }
    std::cout << std::endl;
}

int main() {
    print({1, 2, 3, 4, 5}); // 传入一个初始化列表
    print({10, 20});        // 传入另一个初始化列表
    return 0;
}

输出

代码语言:javascript
代码运行次数:0
复制
1 2 3 4 5
10 20

(2)用于类构造函数

可以使用 std::initializer_list 构造一个类,使其接受列表初始化:

代码语言:javascript
代码运行次数:0
复制
#include <initializer_list>
#include <iostream>
#include <vector>

class MyContainer {
public:
    MyContainer(std::initializer_list<int> values) {
        for (int value : values) {
            _data.push_back(value);
        }
    }

    void print() const {
        for (int value : _data) {
            std::cout << value << " ";
        }
        std::cout << std::endl;
    }

private:
    std::vector<int> _data;
};

int main() {
    MyContainer container{1, 2, 3, 4, 5};
    container.print();
    return 0;
}

输出

代码语言:javascript
代码运行次数:0
复制
1 2 3 4 5

(3)与标准容器结合

可以直接使用 std::initializer_list 初始化标准容器:

代码语言:javascript
代码运行次数:0
复制
#include <vector>
#include <iostream>

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5}; // 使用初始化列表初始化 std::vector
    for (int value : v) {
        std::cout << value << " ";
    }
    std::cout << std::endl;
    return 0;
}

输出

代码语言:javascript
代码运行次数:0
复制
1 2 3 4 5

4. std::initializer_list 的内部实现

std::initializer_list 实际上是一个轻量级的类模板,内部包含一个指针和大小信息:

代码语言:javascript
代码运行次数:0
复制
template<class T>
class initializer_list {
public:
    // 类型定义
    using value_type = T;
    using reference = const T&;
    using const_reference = const T&;
    using size_type = size_t;

    // 获取指针指向的第一个元素
    constexpr const T* begin() const noexcept { return _array; }
    // 获取指针指向的最后一个元素的下一个位置
    constexpr const T* end() const noexcept { return _array + _size; }
    // 获取元素数量
    constexpr size_t size() const noexcept { return _size; }

private:
    // 构造时由编译器自动设置
    const T* _array;
    size_t _size;
};

由此可以看出:

  • 它存储一个指向数组首元素的指针和数组大小,不支持动态增长或修改。
  • 编译器负责管理其生命周期,因此程序员不需要关心内存管理。

5. 注意事项

  • 不可修改内容std::initializer_list 中的内容是常量,不能被修改。
  • 只能用于固定大小的初始化:如果需要动态大小的数据结构,应使用其他容器(如 std::vector)。
  • 隐式生成:不能显式创建 std::initializer_list 对象,必须通过 {} 初始化。

代码语言:javascript
代码运行次数:0
复制
// 这里{"sort", "排序"}会先初始化构造一个pair对象
map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };

2.声明

c++11提供了多种简化声明的方式,尤其是在使用模板时

2.1 auto

在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局部的变量默认就是自动存储类型,所以auto就没什么价值了。C++11中废弃auto原来的用法,将其用于实现自动类型推断。这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初始化值的类型

代码语言:javascript
代码运行次数:0
复制
int main()
{
	int i = 10;
	auto p = &i;
	auto pf = strcpy;
	cout << typeid(p).name() << endl;
	cout << typeid(pf).name() << endl;
	map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };
	//map<string, string>::iterator it = dict.begin();
	auto it = dict.begin();
	return 0;
}
2.2 decltype

decltype 是一个非常强大的工具,尤其在泛型编程和模板编程中,它为类型推导提供了灵活性。它的能力远超 auto,可以精确地获取表达式的类型,尤其适合处理复杂类型和左值/右值差异的场景。

1. 定义与作用

decltype 的功能是检查一个表达式的类型,并返回该类型。

其语法形式为:

代码语言:javascript
代码运行次数:0
复制
decltype(expression)

编译器在编译时会评估 expression,但不会实际计算它,只会确定其类型。


2. 使用场景与示例

(1)推导变量类型 decltype 可以用来推导变量的类型,而无需显式声明。

代码语言:javascript
代码运行次数:0
复制
#include <iostream>

int main() {
    int a = 42;
    decltype(a) b = 10; // b 的类型与 a 相同,即 int
    std::cout << b << std::endl; // 输出 10
    return 0;
}

(2)与 auto 配合使用 虽然 auto 也能推导类型,但有时需要明确指定某个表达式的类型,decltype 更适合。

代码语言:javascript
代码运行次数:0
复制
#include <iostream>

int main() {
    int x = 10;
    double y = 3.14;

    auto z = x + y;          // z 的类型为 double,因为表达式 x + y 的结果是 double
    decltype(x + y) w = 5.5; // 显式指定 w 的类型与 x + y 的类型一致,即 double
    std::cout << w << std::endl; // 输出 5.5
    return 0;
}

(3)用在函数返回类型 在函数返回类型中,可以使用 decltype 推导返回值的类型,特别是在泛型编程中非常有用。

代码语言:javascript
代码运行次数:0
复制
#include <iostream>

int add(int a, int b) {
    return a + b;
}

template <typename T1, typename T2>
auto multiply(T1 a, T2 b) -> decltype(a * b) { // 返回类型由 a * b 的类型决定
    return a * b;
}

int main() {
    std::cout << multiply(2, 3.5) << std::endl; // 输出 7
    return 0;
}

(4)获取成员变量类型 decltype 可用于获取类成员变量的类型。

代码语言:javascript
代码运行次数:0
复制
#include <iostream>

class MyClass {
public:
    int value;
};

int main() {
    MyClass obj;
    decltype(obj.value) x = 42; // x 的类型与 obj.value 相同,即 int
    std::cout << x << std::endl; // 输出 42
    return 0;
}

(5)推导复杂表达式的类型 decltype 可用于推导复杂表达式的类型。

代码语言:javascript
代码运行次数:0
复制
#include <iostream>

int main() {
    int a = 10, b = 20;

    decltype(a + b) sum = a + b; // sum 的类型为 int,因为 a + b 是 int
    decltype((a)) ref_a = a;     // ref_a 的类型为 int&,因为 (a) 是左值表达式
    ++ref_a;                     // 修改 ref_a 会影响 a

    std::cout << a << " " << sum << std::endl; // 输出 11 30
    return 0;
}

注意

  • 如果表达式是左值,decltype 会推导为引用类型。例如 decltype((a))int&
  • 如果表达式是右值,decltype 推导为普通值类型。

3. 与 auto 的区别

特性

auto

decltype

用法

推导变量的类型

推导表达式的类型

推导方式

推导为值类型

区分左值和右值,左值推导为引用类型

示例

auto x = a;

decltype(a) x = a;

适用场景

用于声明变量并自动推导类型

用于获取任意表达式的类型

4. 注意事项

  1. 左值和右值的差异
    • decltype((a)) 是左值表达式,推导为引用类型。
    • decltype(a) 是右值表达式,推导为值类型。
  2. 易与 auto 混淆
    • auto 用于声明变量,不能直接用于表达式类型推导。
    • decltype 专注于类型检查和推导。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-01-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.列表初始化
    • std::initializer_list
  • 2.声明
    • 2.1 auto
    • 2.2 decltype
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档