(学习总结15)C++11小语法与拷贝问题

以下代码环境为 VS2022 C++。

auto关键字

在早期 C/C++ 中 auto 的含义是:使用 auto 修饰的变量,是具有自动存储器的局部变量,不过一般都会隐藏,导致后来不重要了。C++11中,标准委员会赋予了 auto 全新的含义,即:auto 不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto 声明的变量必须由编译器在编译时期推导而得。

  1. 用 auto 声明指针类型时,用 auto 和 auto* 没有任何区别。

  2. 用 auto 声明引用类型时则必须加 &。

  3. 当在同一行用 auto 声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。

  4. auto 不能作为函数的参数,但可以做返回值,不过建议谨慎使用。

  5. auto 不能直接用来声明数组。

#include <iostream>

void test1()
{
	// auto 声明的变量必须初始化,否则报错
	//auto one;
	
	// 以下变量类型由编译器在编译时期推导而得
	auto a = 10;
	auto b = 'C';
	auto c = 1.5;

	// 1.用 auto 声明指针类型,auto 和 auto* 两者没有区别
	auto d = &a;
	auto* e = &a;

	// 2.auto 声明引用类型需要加上 &
	auto& f = a;

	// 3.同一行用 auto 声明多个变量,必须是相同的类型,不然报错
	//auto g = 1, h = 'c', i = 2.5;
	auto j = 1, k = 2, l = 3;

	// 5.auto不能直接用来声明数组,但可声明指针去修改
	//auto m[10] = { 0 };
	//auto n[] = { 1, 2, 3 };
	int arr[] = { 1, 2, 3 };
	auto o = arr;	
	o[0] = 5;
	o[1] = 6;
}

// 4.auto 不能作为函数的参数,
//void test2(auto number)
//{
//	;
//}

// 但可以做返回值,不过建议谨慎使用
auto test3()
{
	return 0;
}

int main()
{
	test1();

	return 0;
}

范围for

对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此 C++11 中引入了基于范围的 for 循环。for 循环后的括号由冒号 “ : ” 分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。

范围 for 可以作用到数组和容器对象上进行遍历,范围 for 的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。

#include <iostream>
#include <vector>
#include <list>

void test1()
{
	int arr1[] = { 1, 2, 3, 4, 5 };
	std::vector<int> arr2 = { 5, 4, 3, 2, 1 };
	std::list<int> arr3 = { 1, 3, 5, 7, 9 };

	// 一般遍历
	int len1 = sizeof(arr1) / sizeof(arr1[0]);
	for (int i = 0; i < len1; ++i)
	{
		std::cout << arr1[i] << " ";
	}
	std::cout << std::endl;

	std::vector<int>::iterator it1 = arr2.begin();
	while (it1 != arr2.end())
	{
		std::cout << *it1 << " ";

		++it1;
	}
	std::cout << std::endl;

	std::list<int>::iterator it2 = arr3.begin();
	while (it2 != arr3.end())
	{
		std::cout << *it2 << " ";

		++it2;
	}
	std::cout << std::endl;

	std::cout << "---------" << std::endl;

	// 范围for本质是替换为迭代器遍历,
	// 只要能用迭代器遍历的容器都能使用范围for
	for (const auto& e : arr1)
	{
		std::cout << e << " ";
	}
	std::cout << std::endl;

	for (const auto& e : arr2)
	{
		std::cout << e << " ";
	}
	std::cout << std::endl;

	for (const auto& e : arr3)
	{
		std::cout << e << " ";
	}
	std::cout << std::endl;
}

int main()
{
	test1();

	return 0;
}

initializer_list

为了使 C++ STL 中的容器支持与数组一样的使用 " { 数据1, 数据2… } " 初始化,在 C++11 引入了 initializer_list 类。

// 数组使用 { } 初始化
int arr1[5] = { 1, 2, 3, 4, 5 };	
int arr2[] = { 5, 4, 3, 2, 1 };

详细可参考:std::initializer_list

" { } " 内的数据是在栈上开辟存储的,而 initializer_list 类里封装有两个指针,一个指向 " { } " 第一个元素,另一个指向最后一个元素下一个位置,在 VS2022 调试时可以看到:
在这里插入图片描述
并且其中所有元素都加上了 const,表示 " 只读 ",无法修改。

因此使用 C++ STL 的容器初始化时,在 initializer_list 类的基础上可以使用 { } 初始化:

#include <iostream>
#include <vector>
#include <deque>
#include <list>
#include <map>
#include <set>

void test1()
{
	std::vector<int> 	arr1 = { 1, 2, 3, 4, 5 };
	std::list<int> 		arr2 = { 1, 2, 3, 4, 5 };
	std::string 		arr3 = { 'a', 'b', 'c', 'd', '\0' };
	std::deque<int> 	arr4 = { 1, 2, 3, 4, 5 };
	std::map<int, char> arr5 = {{1, 'a'}, {2, 'b'}, {3, 'c'}, {4, 'd'}, {5, 'e'}};
	std::set<int> 		arr6 = { 1, 2, 3, 4, 5 };
}

int main()
{
	test1();

	return 0;
}

若要使自己的容器也能支持 " { } " 方式初始化,需要在类中单独添加一个为 initializer_list 类准备的构造函数。

这里以这篇博客为例:(C++ STL)vector类的简单模拟实现与源码展示

在类中增加以 initializer_list 类参数的构造函数即可:

template<class Type>					// 若类中已经有代表类型的关键字,
vector(std::initializer_list<Type> il)	// 可去掉 template<class Type> 和
{										// 替换 std::initializer_list<Type> 中的 Type
    for (const auto& e : il)			// initializer_list 支持迭代器访问
    {
        push_back(e);					// my::vector<T> 的成员函数
    }
}

深拷贝与浅拷贝

浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。
在这里插入图片描述

可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。
在这里插入图片描述
如果 C++ 一个类中涉及到资源的管理,其 拷贝构造函数赋值运算符重载 以及 析构函数 必须要显式给出。一般情况都是按照深拷贝方式提供。

写时拷贝

写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。

引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成 1,每增加一个对象使用该资源,就给计数增加 1,当某个对象被销毁时,先给该计数减 1,然后再检查是否需要释放资源,如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源。

具体细节可参考这位大佬的文章:
C++ STL string的Copy-On-Write技术
C++的std::string的“读时也拷贝”技术!

  • 28
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值