C++11中隐式类型转换的实现示例

 更新时间:2024年06月23日 10:48:54   作者:有趣的中国人  
C++类型转换分为:隐式类型转换和显式类型转换,本文主要介绍了C++11中隐式类型转换的实现示例,具有一定的参考价值,感兴趣的可以了解一下

1. 关于C++11

1.1 C++11简介

在2003年C++标准委员会曾经提交了一份技术勘误表(简称TC1), 使得 C++03 这个名字已经取代了 C++98 成为 C++11 之前的最新C++标准名称

不过由于 C++03(TC1) 主要是对 C++98 标准中的漏洞进行修复,语言的核心部分则没有改动,因此人们习惯性的把两个标准合并称为 C++98/03 标准。从 C++0x 到C++11,C++ 标准10年磨一剑,第二个真正意义上的标准珊珊来迟。

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

1.2 C++11官方介绍

🎉官网链接: C++11

2. 统一的列表初始化

2.1 C/C++98 数组 & 结构体 初始化

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

struct Point
{
	int _x;
	int _y;
};

int main()
{
	// struct Point类型的初始化
	Point p = { 100, 99 };
	int arr1[10] = { 10, 20, 30 };
	return 0;
}

2.2 单/多参数类型的隐式类型转换

对于自定义类型,如果是单参数类型的构造函数,会发生隐式类型转换,例如:

class A
{
public:
	A(int x)
		: _x(x)
		, _y(x)
	{}

	void Print()
	{
		cout << _x << ":" << _y << endl;
	}

private:
	int _x;
	int _y;
};

int main()
{
	// 隐式类型转换
	A aa1 = 1;
	A aa2 = { 2 };
	
	// 直接构造
	A aa3(3);
	
	// 也可以省略等号
	A aa4{ 1 };
	return 0;
}

如果是多参数类型的构造函数,同样会发生隐式类型转换,例如:

class A
{
public:
	A(int x, int y)
		: _x(x)
		, _y(y)
	{}
	
private:
	int _x;
	int _y;
};

int main()
{
	// 隐式类型转换
	A aa1 = { 1, 2 };
	
	// 也可以省略等号
	A aa2{ 3, 4 };

	// 可以省略等号
	A aa3(4, 5);

	return 0;
}

如果不想发生隐式类型的转换,可以在构造函数之前加上 explicit 关键字,例如:

class A
{
public:
	// 不允许发生隐式类型转换
	explicit A(int x, int y)
		: _x(x)
		, _y(y)
	{}
	
	// 不允许发生隐式类型转换
	explicit A(int x)
		: _x(x)
		, _y(x)
	{}

	void Print()
	{
		cout << _x << ":" << _y << endl;
	}

private:
	int _x;
	int _y;
};

int main()
{
	// 下面两行会报错
	A aa0 = 1;
	A aa3 = { 1 };
	A aa1 = { 1, 2 };
	
	// 以下可以正常使用
	A aa2{ 3, 4 };
	A aa4{ 0 };
	
	return 0;
}

关于单/多参数类型的构造函数的解释:

对于形如 A aa1 = { 1, 2 };的构造,编译器处理时会发生以下两个步骤:

首先会利用 { } 中的值调用构造函数生成一个临时变量;

对 aa1 进行拷贝构造。

但是编译器会优化成直接构造。

对于形如 A aa2{ 3, 4 };的构造,就会直接调用构造函数,不会产生临时变量。

如果产生了临时变量,我们知道临时变量具有常性,就有:

  • 如果用:A& aa1 = { 1, 2 };会报错。
  • 得用:const A& aa1 = { 1, 2 };

3. STL 中的 Initializer_list 构造函数

我们这里先以 vector 为例。

3.1 Initializer_list 

initializer_list 是 C++11 引入的一种标准库类型,用于方便地初始化同一种类型的元素列表。initializer_list 允许通过大括号 { } 语法进行初始化,例如:

std::initializer_list<int> my_list = {1, 2, 3, 4, 5};

这里 my_list 是一个包含了整数元素 {1, 2, 3, 4, 5} 的 initializer_list<int> 对象。

主要特点包括:

  • 不可变性: 一旦初始化完成,initializer_list 中的元素不可再被修改。
  • 轻量:initializer_list 本身只包含指向数据的指针和长度信息,因此非常轻量,适合在函数参数传递和对象构造时使用。
  • 语法简洁: 通过 { } 初始化列表的语法,能够清晰地指定一组初始值,而不需要显式地调用构造函数。

底层实现逻辑:

namespace std 
{
    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;

        initializer_list() noexcept; // 构造一个空的 initializer_list
        size_type size() const noexcept; // 返回列表中元素的个数
        const T* begin() const noexcept; // 返回指向第一个元素的指针
        const T* end() const noexcept; // 返回指向最后一个元素之后的位置的指针
    };
}

3.2 Initializer_list 初始化容器 

当使用vector时,每次都用不同数量的值初始化容器,那么就要写非常多的构造函数来实现这个需求。

但是用Initializer_list就可以一劳永逸的解决这个问题。

int main()
{
	// 用 10 个 1 构造 vector
	vector<int> v(10, 1);

	initializer_list<int> il = { 10,9,8,5 };
	vector<int>(il);
	
	vector<int> v1 = { 1,2,3,4,5,6 };
	vector<int> v2({ 9,8,7,6,5,4 });

	return 0;
}

两种构造的区别:

对于:vector<int> v1 = { 1,2,3,4,5,6 };

  • 编译器会先将{ 1,2,3,4,5,6 }识别成initializer_list
  • 然后调用Initializer_list的构造,中间生成临时变量;
  • 最后拷贝构造给v1
  • 但是编译器会优化成直接构造。

对于:vector<int> v2({ 9,8,7,6,5,4 });

  • 这就是直接构造。
  • 总的来说,形如:X自定义 = Y类型 一定发生了 隐式类型转换。X 支持 Y 为参数类型构造就可以。

对于 map 的 initializer_list 的构造

map 支持 pair 类型的 initializer_list 的构造:

在这里插入图片描述

这里的 value_type 就是 pair。

map构造代码示意:

int main()
{
	// 方法一:
	pair<string, string> kv1 = { "include", "包括" };
	pair<string, string> kv2 = { "sort","排序" };
	map<string, string> dict1({ kv1, kv2 });

	// 方法二:
	map<string, string> dict2 = { kv1, kv2 };
	map<string, string> dict3 = {{"include", "包括"}, { "sort","排序" }};
	
	return 0;
}

两种构造区别:

对于方法一:

  • 因为pair支持双参数的构造函数,所以{ "include", "包括" }、 { "sort","排序" }会发生隐式类型的转换生成临时变量,然后再拷贝构造给kv1kv2(编译器会优化成直接构造);
  • 因为map 支持 pair 类型的 initializer_list 的构造,因此直接使用:map<string, string> dict1({ kv1, kv2 });即可

对于方法二:

  • {"include", "包括"}, { "sort","排序" }会被识别成两个pair类型;
  • 然后外层大括号会被识别成initializer_list,进行隐式类型转换,在进行拷贝构造(会被优化)。

到此这篇关于C++11中隐式类型转换的实现示例的文章就介绍到这了,更多相关C++11 隐式类型转换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • C语言如何读取bmp图像

    C语言如何读取bmp图像

    这篇文章主要介绍了C语言如何读取bmp图像,BMP即bitmap,由文件头信息块、图像描述信息块、颜色表、图像数据区四部分组成,下文更多相关资料需要的小伙伴可以参考一下
    2022-04-04
  • C++函数pyrUp和pyrDown来实现图像金字塔功能

    C++函数pyrUp和pyrDown来实现图像金字塔功能

    这篇文章主要介绍了C++函数pyrUp和pyrDown来实现图像金字塔功能,如何使用OpenCV函数 pyrUp 和 pyrDown 对图像进行向上和向下采样,需要的朋友可以参考下
    2017-03-03
  • c语言实现单链表算法示例分享

    c语言实现单链表算法示例分享

    这篇文章主要介绍了c语言实现单链表算法示例,需要的朋友可以参考下
    2014-02-02
  • c++重载运算符时返回值为类的对象或者返回对象的引用问题

    c++重载运算符时返回值为类的对象或者返回对象的引用问题

    这篇文章主要介绍了c++重载运算符时返回值为类的对象或者返回对象的引用问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • C语言内存操作函数详解

    C语言内存操作函数详解

    这篇文章主要介绍了C语言全部内存操作函数的实现详细讲解,作者用图文代码实例讲解的很清晰,有感兴趣的同学可以研究下
    2021-10-10
  • EasyC++静态持续变量

    EasyC++静态持续变量

    这篇文章主要介绍了EasyC++静态持续变量,除了自动存储变量之后,C++当中还有静态持续变量。关于静态持续变量的定义C++和C语言是一样的,它拥有三种链接性,即外部链接性、内部连接性和无链接性,下面一起进入文章了解更具体内容吧
    2021-12-12
  • C++多继承多态的实例详解

    C++多继承多态的实例详解

    这篇文章主要介绍了C++多继承多态的实例详解的相关资料,需要的朋友可以参考下
    2017-06-06
  • 总结C/C++面试中可能会碰到的字符串指针题

    总结C/C++面试中可能会碰到的字符串指针题

    C/C++是最能体现程序员能力的语言之一,其功能强大,在IT行业的各个方面都有大量的应用。下面这篇文章主要介绍了总结了在C/C++面试中可能会碰到的字符串指针题,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-01-01
  • 用C语言求解第N项斐波那契数列问题

    用C语言求解第N项斐波那契数列问题

    这篇文章主要介绍了用C语言求解第N项斐波那契数列问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • C语言读取和存储bmp格式图片

    C语言读取和存储bmp格式图片

    这篇文章主要为大家详细介绍了C语言读取和存储bmp格式图片,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-10-10

最新评论