C++ 11

目录

1. 统一的列表初始化

1.1 {}初始化

1.2 std::initializer_list

2. decltype

3. 右值引用和移动语义

3.1 左值引用和右值引用

3.2 左值引用与右值引用比较

3.3 右值引用使用场景和意义

3.4 右值引用引用左值及其一些更深入的使用场景分析

3.5 完美转发

4 新的类功能


1. 统一的列表初始化

1.1 {}初始化

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

struct Point
{
	int x;
	int y;
};

int main()
{
	int arr1[] = { 1, 2, 3 };
	int arr2[2] = { 0 };
	Point p = { 1, 2 };
	return 0;
}

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

struct Point
{
	int x;
	int y;
};

int main()
{

	int arr1[]{ 1, 2, 3 };
	int arr2[2]{ 0 };
	Point p{ 1, 2 };

	int* ptr = new int[4]{0};
	return 0;
}

new的对象也可以用{}初始化

class Date
{
public:
	Date(size_t year, size_t month, size_t day)
		:_year(year),
		_month(month),
		_day(day)
	{}
private:
	size_t _year;
	size_t _month;
	size_t _day;
};

int main()
{
	Date d1(2024, 3, 18);
	Date d2 = { 2024, 3, 18 };
	Date d2{ 2024, 3, 18 };
	return 0;
}

对于自定义类型,第一个()是构造,下面两个{}是利用列表初始化。

1.2 std::initializer_list

int main()
{
	auto il = { 1,2,3 };
	cout << typeid(il).name() << endl;
	initializer_list<int> il2 = { 1,2,3 };
	return 0;
}

std::initializer_list使用场景:

std::initializer_list一般是作为构造函数的参数,C++11对STL中的不少容器就增加 std::initializer_list作为参数的构造函数,这样初始化容器对象就更方便了。

也可以作为operator= 的参数,这样就可以用大括号赋值。

int main()
{
	auto il = { 1,2,3 };
	cout << typeid(il).name() << endl;
	initializer_list<int> il2 = { 1,2,3 };

	list<int> lt = { 1,2,3 };
	initializer_list<char> il4 = { 'a', 'b', 'c' };
	string s = { 'a', 'b', 'c' };
	string s2 = { "hello word"};

	initializer_list<const char*> il3 = { "hello word" , "abc"};

	map<int, int> m = { make_pair(1,1), make_pair(2,2) ,make_pair(3,3) };
	return 0;
}

对于string类型字符串类型的初始化建议还是别用,容易出bug,initializer_list<const char*>里面只有一个还行,如果大于一个就会错误,直接构造就挺好。

容器构造和赋值的实现

namespace kele
{
    template<class T>
    class vector {
    public:
        typedef T* iterator;
        iterator begin(){return _start;}
        iterator end(){return _finish;}
 
        vector(initializer_list<T> l)
        {
            _start = new T[l.size()];
            _finish = _start + l.size();
            _endofstorage = _start + l.size();
            iterator vit = _start;
            
            for (auto e : l)
               *vit++ = e;
        }
        vector<T>& operator=(initializer_list<T> l) {
            vector<T> tmp(l);
            swap(_start, tmp._start);
            swap(_finish, tmp._finish);
            swap(_endofstorage, tmp._endofstorage);
            return *this;
        }
    private:
        iterator _start;
        iterator _finish;
        iterator _endofstorage;
    };
}

int main()
{
    kele::vector<int> v = { 1,2,3 };
    for (auto e : v)
    {
        cout << e << endl;
    }
    return 0;
}

2. decltype

关键字decltype将变量的类型声明为表达式指定的类型。

template <typename _Tx, typename _Ty>
auto multiply(_Tx x, _Ty y)
{
	return x * y;
}

int main()
{
	int x = 1;
	char y = 'a';
	decltype(x * y) z;//z int
	cout << typeid(z).name() << endl;

	double a = 0.25;
	decltype(multiply(a, x)) m;//m double
	cout << typeid(m).name() << endl;
	return 0;
}


3. 右值引用和移动语义

3.1 左值引用和右值引用

传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,所以从现在开始我们 之前学习的引用就叫做左值引用。无论左值引用还是右值引用,都是给对象取别名

什么是左值?什么是左值引用?

左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋 值,左值可以出现赋值符号的左边右值不能出现在赋值符号左边。定义时const修饰符后的左 值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。

int main()
{
	// 以下的p、b、c、*p都是左值
	int* p = new int(0);
	int b = 1;
	const int c = 2;

	// 以下几个是对上面左值的左值引用
	int*& rp = p;
	int& rb = b;
	const int& rc = c;
	int& pvalue = *p;
	return 0;
}

什么是右值?什么是右值引用?

右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值,匿名对象(这个不能是左值引 用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能 取地址。右值引用就是对右值的引用,给右值取别名。

int main()
{
	double x = 1.1, y = 2.2;
	// 以下几个都是常见的右值
	10;
	x + y;
	fmin(x, y);
	// 以下几个都是对右值的右值引用
	int&& rr1 = 10;
	double&& rr2 = x + y;
	double&& rr3 = fmin(x, y);
	// 这里编译会报错:error C2106: “=”: 左操作数必须为左值
	10 = 1;
	x + y = 1;
	fmin(x, y) = 1;
	return 0;
}

需要注意的是右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置,且可以取到该位置的地址,也就是说例如:不能取字面量10的地址,但是rr1引用后,可以对rr1取地址,也可以修改rr1。如果不想rr1被修改,可以用const int&& rr1 去引用

3.2 左值引用与右值引用比较

左值引用只能引用左值,不能引用右值。

但是const左值引用既可引用左值,也可引用右值。

右值引用只能右值,不能引用左值。

但是右值引用可以move以后的左值。

int main()
{
	int x = 10;
	int& z = x;//左值引用
	int&& y = 10;//右值引用

	int&& yy = move(x);//右值引用左值
	const int& xx = 10;//左值引用右值
	return 0;
}

3.3 右值引用使用场景和意义

左值引用的使用场景:做参数和做返回值都可以提高效率。(对于自定义类型)

string& func(const string& x)
{}

左值引用的短板:但是当函数返回对象是一个局部变量,出了函数作用域就不存在了,就不能使用左值引用返回, 只能传值返回。

例如: string operator+(const string& str) 

在C++98,tmp是函数内部的局部对象,所以只能拷贝构造返回值,这是一次深拷贝,不能用引用。然后s2又是一次拷贝构造,深拷贝。(编译器不优化的前提下)

这个返回值是一个临时对象,也就是右值,将亡值,这个临时对象在拷贝之后也是要析构的,可不可以利用一下这个资源呢?

右值引用和移动语义解决上述问题:

在bit::string中增加移动构造,移动构造本质是将参数右值的资源窃取过来,占位已有,那么就不用做深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自己。

不仅仅有移动构造,还有移动赋值

namespace kele
{
    class string
    {
    public:
        string(const char* str = "")//构造
            :_size(strlen(str))
        {
            cout << "构造" << endl;
            _capacity = _size == 0 ? 3 : _size;
            _str = new char[_capacity + 1];
            strcpy(_str, str);
        }

        string(const string& str)//拷贝构造
            :_str(nullptr)
        {
            cout << "string(const string & str)//拷贝构造" << endl;
            string tmp(str._str);
            swap(tmp);
        }

        string(string&& str)//移动构造
            :_str(nullptr),
            _size(0),
            _capacity(0)
        {
            cout << "string(string&& str)//移动构造" << endl;
            swap(str);
        }

        string& operator=(string& str)//赋值重载
        {
            if (this != &str)
            {
                cout << "string& operator=(string str)//赋值重载" << endl;
                string tmp(str._str);
                swap(tmp);
                return *this;
            }
        }

        string& operator=(string&& str)//移动赋值重载
        {
            if (this != &str)
            {
                cout << "string& operator=(string&& str)//移动赋值重载" << endl;
                swap(str);
                return *this;
            }
        }

        void reserve(size_t n = 0)
        {
            if (n > _capacity)
            {
                char* tmp = new char[n + 1];
                strcpy(tmp, _str);
                delete[] _str;
                _str = tmp;
                _capacity = n;
            }
        }
        string& operator+=(const string& s)
        {
            size_t n = s._size;
            if (_size + n > _capacity)
            {
                reserve(_capacity + n);
            }
            strcpy(_str + _size, s._str);
            _size += n;
            return *this;
        }

        string operator+(const string& str)
        {
            string tmp(_str);
            tmp += str;
            return tmp;
        }

        ~string()
        {
            delete[] _str;
            _size = _capacity = 0;
        }

        void swap(string& s)
        {
            std::swap(_str, s._str);
            std::swap(_size, s._size);
            std::swap(_capacity, s._capacity);
        }
    private:
        char* _str;
        size_t _size;
        size_t _capacity;
    };
}

STL中的容器都是增加了移动构造和移动赋值:

3.4 右值引用引用左值及其一些更深入的使用场景分析

当需要用右值引用引用一个左值时,可以通过move函数将左值转化为右值。

C++11中,std::move()函数位于头文件中,该函数名字具有迷惑性, 它并不搬移任何东西,唯一的功能就是将一个左值强制转化为右值引用,然后实现移动语义。

STL容器插入接口函数也增加了右值引用版本:

3.5 完美转发

模板中的&& 万能引用:

模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值

模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力,但是

引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值

我们希望能够在传递过程中保持它的左值或者右值的属性, 就需要用我们下面学习的完美转发

void Fun(int& x) { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }

void Fun(int&& x) { cout << "右值引用" << endl; }
void Fun(const int&& x) { cout << "const 右值引用" << endl; }

template<typename T>
void PerfectForward(T&& t)
{
    //Fun(t);
	Fun(forward<T>(t));完美转发
}
int main()
{
	PerfectForward(10);           // 右值
	int a;
	PerfectForward(a);            // 左值
	PerfectForward(std::move(a)); // 右值
	const int b = 8;
	PerfectForward(b);      // const 左值
	PerfectForward(std::move(b)); // const 右值
	return 0;
}

std::forward 完美转发在传参的过程中保留对象原生类型属性

完美转发可能在真实使用场景中需要多次使用,因为右值引用在接收后都退化成了左值。


4 新的类功能

默认成员函数

原来C++类中,有6个默认成员函数:

  1.  构造函数
  2.  析构函数
  3.  拷贝构造函数
  4.  拷贝赋值重载
  5.  取地址重载
  6.  const 取地址重载

C++11 新增了两个:移动构造函数和移动赋值运算符重载。

默认移动构造生成的条件:没有自己实现移动构造函数且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个

默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝自定义类型成员,则需要看这个成员是否实现移动构造, 如果实现了就调用移动构造,没有实现就调用拷贝构造。

强制生成默认函数的关键字default:

Person(Person&& p) = default;

禁止生成默认函数的关键字delete:

Person(const Person& p) = delete;

继承和多态中的finaloverride关键字

在C++中,final是一个关键字,用于修饰类、成员函数或虚函数,表示它们不能被继承或重写。具体来说,final关键字有以下几个用途:

final修饰类:当一个类被声明为final时,该类不能被其他类继承。这样可以防止其他类对该类进行派生,保护该类的实现和接口不被修改。

final修饰成员函数:当一个成员函数被声明为final时,该函数不能在派生类中被重写。这样可以确保该函数的实现不会被修改。

final修饰虚函数:当一个虚函数被声明为final时,该虚函数不能在派生类中被重写。这样可以阻止派生类对该虚函数的进一步修改。

使用final关键字可以提高代码的安全性和可维护性,同时也能够更好地表达设计意图

override(重写覆盖): 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。


未完待续

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/468456.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

长安链开源社区发布2023年度长安链优秀应用案例

1月27日结束的“长安链发布三周年庆暨生态年会”上&#xff0c;在国家区块链技术创新中心的指导下&#xff0c;长安链开源社区联合长安链生态联盟正式发布2023年度长安链行业示范案例、领域精品案例及特色创新案例。 本次评选面向2023年度应用长安链上线并取得应用成效的案例&…

国内ip地址范围多大?ip地址容易切换吗?

随着互联网的蓬勃发展&#xff0c;IP地址成为连接每一台设备和服务的纽带。对于一个庞大的网络市场而言&#xff0c;了解国内IP地址的范围及其背后的技术细节显得尤为重要。虎观代理小二将深入剖析国内IP地址的范围&#xff0c;带您走进这个庞大而复杂的网络世界。 什么是IP地…

BlenderGIS 快捷键E 报错问题 Report: Error

最新版的Blender4.0 对于 BlenderGIS2.28版本的插件不兼容&#xff0c;BlenderGIS2.28兼容Blender3.6.9及之前的版本&#xff0c;应该是BlenderGIS插件很久没更新了导致的。

Pytorch详细应用基础(全)

&#x1f525;博客主页&#xff1a; A_SHOWY&#x1f3a5;系列专栏&#xff1a;力扣刷题总结录 数据结构 云计算 数字图像处理 力扣每日一题_ 1.安装pytorch以及anaconda配置 尽量保持默认的通道&#xff0c;每次写指令把镜像地址写上就行。 defaults优先级是最低的&#…

【LeetCode 算法刷题笔记-路径篇】

1.0112. 路径总和 1.1 题目大意 描述&#xff1a;给定一个二叉树的根节点 root 和一个值 targetSum。 要求&#xff1a;判断该树中是否存在从根节点到叶子节点的路径&#xff0c;使得这条路径上所有节点值相加等于 targetSum。如果存在&#xff0c;返回 True&#xff1b;否则…

GPT-SoVITS语音合成服务器部署,可远程访问(全部代码和详细部署步骤)

GPT-SoVITS 是一个开源项目&#xff0c;它使用大约一分钟的语音数据便可以训练出一个优秀的TTS模型。 项目的核心技术是 Zero-shot TTS 和 Few-shot TTS。 Zero-shot TTS 可以让用户输入5秒钟的语音样本并立即体验转换后的语音&#xff0c;而 Few-shot TTS 则可以通过使用仅一…

《论文阅读》EmpDG:多分辨率交互式移情对话生成 COLING 2020

《论文阅读》EmpDG:多分辨率交互式移情对话生成 COLING 2020 前言简介模型架构共情生成器交互鉴别器损失函数前言 亲身阅读感受分享,细节画图解释,再也不用担心看不懂论文啦~ 无抄袭,无复制,纯手工敲击键盘~ 今天为大家带来的是《EmpDG: Multi-resolution Interactive E…

Java 面向对象编程进阶三(封装)

目录 封装(encapsulation) 封装的作用和含义 封装的实现—使用访问控制符 public 访问权限修饰符&#xff1a; protected 访问权限修饰符&#xff1a; 默认访问权限修饰符 private 访问权限修饰符 封装的一些处理 封装(encapsulation) 封装是面向对象三大特征之一。对于…

蓝牙耳机哪个品牌最好?2024口碑绝佳蓝牙耳机推荐分享

​随着生活水平的不断提高&#xff0c;蓝牙耳机已成为我们生活中不可或缺的一部分。它为我们提供了极大的便利&#xff0c;无论是在听音乐、观看视频还是进行电话通话时。面对市场上种类繁多的蓝牙耳机&#xff0c;我为你整理了几款表现很不错的耳机产品&#xff0c;希望能帮助…

亚信安慧AntDB数据库分布式架构剖析之snapshot receiver进程

本文主要介绍亚信安慧AntDB数据库的分布式架构下的特有进程之snapshot receiver的设计&#xff0c;这也是分布式架构的核心进程之一。 进程简介 该进程的作用从逻辑上解释包含两个方面&#xff1a; 同步快照&#xff0c;并且是作为通信的client端存在 同步事务号&#xff0c;…

【博客7.4】缤果Qt5_TWS串口调试助手V2.0 (高级篇)

超级好用的Qt5_TWS耳机串口调试助手 开发工具: qt-opensource-windows-x86-5.14.2 (编程语言C) 目录 前言 一、软件概要&#xff1a; 二、软件界面&#xff1a; 1.App演示 三、获取 >> 源码以及Git记录&#xff1a; 总结 前言 串口调试助手支持常用的50bps - 10M…

MyBatisPlus 之四:MP 的乐观锁和逻辑删除、分组、排序、链式的实现步骤

乐观锁 乐观锁是相对悲观锁而言的&#xff0c;乐观锁假设数据一般情况不会造成冲突&#xff0c;所以在数据进行提交更新的时候&#xff0c;才会正式对数据的冲突与否进行检测&#xff0c;如果冲突&#xff0c;则返回给用户异常信息&#xff0c;让用户决定如何去做。 乐观锁适用…

WannierTools安装教程

wanniertools简单介绍 开源软件包WannierTools&#xff0c;是一个用于研究新型拓扑材料的软件。此代码在紧束缚模型中工作&#xff0c;可以由另一个软件包Wannier90生成。 它可以通过计算Wilson loop来帮助对给定材料的拓扑相进行分类&#xff0c;通过角分辨光电发射(ARPES)和…

掘根宝典之C++正向迭代器和反向迭代器详解

简介 迭代器是一种用于遍历容器元素的对象。它提供了一种统一的访问方式&#xff0c;使程序员可以对容器中的元素进行逐个访问和操作&#xff0c;而不需要了解容器的内部实现细节。 C标准库里每个容器都定义了迭代器&#xff0c;这迭代器的名字就叫容器迭代器 迭代器的作用类…

后端系统开发之——创建注册接口

原文地址&#xff1a;后端系统开发之——创建注册接口 - Pleasure的博客 下面是正文内容&#xff1a; 前言 这是一篇SpringBoot项目的实践篇。 主要用于介绍如何从零开始搭建某一种类型的系统。 个人认为&#xff0c;只要后端逻辑完善了&#xff0c;纵使前端页面千变万化都可…

硬件基础:带缓启动MOS管电源开关电路

电源开关电路&#xff0c;经常用在各“功能模块”电路的电源通断控制&#xff0c;是常用电路之一。 本文要讲解的电源开关电路&#xff0c;是用MOS管实现的&#xff0c;且带缓开启功能&#xff0c;非常经典。 一、电路说明 电源开关电路&#xff0c;尤其是MOS管电源开关电路…

2024-3-18-C++day6作业

1>思维导图 2>试编程 要求: 封装一个动物的基类&#xff0c;类中有私有成员&#xff1a;姓名&#xff0c;颜色&#xff0c;指针成员年纪 再封装一个狗这样类&#xff0c;共有继承于动物类&#xff0c;自己拓展的私有成员有&#xff1a;指针成员&#xff1a;腿的个数&a…

蓝桥杯-24点-搜索

题目 思路 --暴力递归全组合的方法。只有4个数&#xff0c;4种计算方式&#xff0c;共有4 * 3 * 2 * 1 * 4种不同的情况&#xff0c;可以写递归来实现。 --每次计算都是两个数之间的运算&#xff0c;因此4个数需要3次计算&#xff0c;第一次计算前有4个数&#xff0c;第二次有…

Mysql与MyBatis

1 Sql语句 增删改查 1.1 建表 -- cmd展示数据库 show databases ; -- cmd登录数据库 mysql localhost -u root -p-- auto_increment 自动增长&#xff0c;每添加一个表项id自动增1 -- char定长字符串 0-255&#xff0c;不足十个字符按十个字符算&#xff0c; varchar变长字符串…

阿里云服务器“地域”,这么选择就对了!

阿里云服务器地域选择方法&#xff0c;如何选择速度更快、网络延迟更低的地域节点&#xff0c;地域指云服务器所在的地理位置区域&#xff0c;地域以城市划分&#xff0c;如北京、杭州、深圳及上海等&#xff0c;如何选择地域&#xff1f;建议根据用户所在地区就近选择地域&…