当前位置: 首页 > news >正文

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

C++11小语法与拷贝问题

  • auto关键字
  • 范围for
  • initializer_list
  • 深拷贝与浅拷贝
  • 写时拷贝

以下代码环境为 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本质是替换为迭代器遍历,// 只要能用迭代器遍历的容器都能使用范围forfor (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> 中的 Typefor (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的“读时也拷贝”技术!


http://www.mrgr.cn/news/17269.html

相关文章:

  • 【编程底层思考】线程阻塞时一定会释放cpu吗
  • 2024年8月文章一览
  • 【MySQL】事务
  • 全国大学生数学建模比赛——关联规则
  • Javascript常见面试手写题
  • DrugAgent:多智能体系统,新药研发速度提升10倍
  • 深入剖析淘宝商品详情API的调用与返回值
  • 【C++】解析friend关键字:概念、友元函数与类的使用
  • 网络安全入门教程(非常详细)从零基础入门到精通,看完这一篇就够了。
  • 八、百度到的--centos安装artifactory--没什么用
  • 数据集格式转化
  • 《机器学习》周志华-CH5(神经网络)
  • 将二叉搜索树转化为排序的双向链表
  • Java 异常架构Exception(异常)
  • 操作符详解(下)
  • 【微机原理】指令JZ和JNZ的区别
  • How to run a JAR file
  • 龙芯+FreeRTOS+LVGL实战笔记(新)——00关于本专栏
  • 干货分享|分享一款高效的软件卸载神器 Geek Uninstaller
  • 四足机器人控制算法——建模、控制与实践(unitree_guide配置)