栏目分类:
子分类:
返回
文库吧用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
文库吧 > IT > 软件开发 > 后端开发 > Java

C++新特性笔记(1)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++新特性笔记(1)

1.左值引用和右值引用

左值是某种存储支持的变量,右值是临时变量或可读但没有固定寻址方法的变量。

左值有具体的地址可以访问,右值有地址但没有固定寻址方法,右值是一种优化技巧。

左值引用只能接受左值,若加上const修饰就可以接收右值,但实际上用const修饰以后左值引用的变量以后,引用得来的变量就变成了一个不可修改的右值。

右值分纯右值,和将亡值。纯右值是只字面值、算数表达式、返回非引用类型的函数调用等等的数据值;将亡值是c++11新引入的类型与右值引用(移动语义)相关的值类型。

用在函数传参和函数返回值时,左值引用避免了对象的拷贝

void get(int& a)//只接收左值,get(10)会报错,因为10是一个右值
void get(const int& a)//可以接收左值和右值,但是a无法再被修改

右值引用只能接受右值,语法上是再加上一个&,右值引用的好处是,当我们输入值为右值时,我们知道它只是一个临时创建的变量,我们可以放心对其进行修改。

右值引用实现了移动语义和完美转发

void get(int&& x)//右值引用,get(10)也不会报错

void get_string1(string&& s)
{
	s = "修改";
}
void get_string2(string&& s)
{
   string* change;
	change = &s;
	*change = "硬改";
}
int main()
{
   string s1 = "不";
   string s2 = "修改";
   //get_string(s1) 报错,因为s1是一个左值
   get_string1(s1+s2);
   cout << s1 + s2 < 

虽然右值无法直接访问地址,但是确实有地址的,可以通过间接的方式获取其内存地址

void get(int&& x,int*& i)
{
	i = &x;
	cout << i << endl;
}

int main()
{
	int* i = nullptr;
	get(10,i);
	cout << i << endl;
	cout << *i << endl;
}

右值引用可以用在move语义的状态,实现简单的move效果

class String {
public:
	String() = default;
	String(const char* string)
	{
		std::cout << "create!" << std::endl;
		m_Size = strlen(string);
		m_Data = new char[m_Size];
		memcpy(m_Data, string, m_Size);
	}
	
	String(const String& other)
	{
		std::cout << "copy!" << std::endl;
		m_Size = other.m_Size;
		m_Data = new char[m_Size];
		memcpy(m_Data, other.m_Data, m_Size);
	}

	String(String&& other) noexcept
	{
		std::cout << "move!" << std::endl;
		m_Size = other.m_Size;
		m_Data = other.m_Data;

		other.m_Size = 0;
		other.m_Data = nullptr;
	}
	~String()
	{
		std::cout << "delete!" << std::endl;
		delete m_Data;
	}
	void print() const
	{
		for (int i = 0; i < m_Size; i++) printf("%c", m_Data[i]);
		printf("n");
	}
private:
	char* m_Data;
	int m_Size;
};


class A {
public:
	A() = default;
	A(const String& s):s(s){}
	A(String&& s) :s((String&&)s) {}
	void print()
	{
		s.print();
	}
	
	
private:
	String s;
};

如果不使用右值引用的拷贝构造函数,在A a(“拷贝”)这样的对象实例化的时候,实际上先调用了有参构造函数,再调用了拷贝构造函数。
开辟了两个堆再依次销毁,浪费性能,那么我们可以使用右值引用实现一个简单的move效果(实际上做了一个浅拷贝的工作)。

A(String&& s) :s(std::move(s)) {}//(String&&)很少用,一般直接用move

move()函数实际上就是将左值转为右值。

在不用右值引用时,函数模板不能完美地转发给内部调用函数,要么只能给内部的其他函数,被转发的参数只能是右值或左值其中之一,不能达到泛型的目的,使用右值引用后就能解决这个问题,达到完美转发。

完美转发实现函数时std::forward,std::forward可以保持原来的值属性不变。

#include 


void reference(int &v){
    printf("左值n");
}

void reference(int &&v){
    printf("右值n");
}

template 
void pass(T && v){
    reference(v);
    reference(std::forward(v));
}

std::forward内部实现:

template
constexpr _Tp&&forward(typename std::remove_reference<_Tp>::type& __t) noexcept{
	return static_cast<_Tp&&>(__t);
}

template
constexpr _Tp&&forward(typename std::remove_reference<_Tp>::type&& __t)noexcept{
	static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument substituting _Tp is an lvalue reference type");
	return static_cast<_Tp&&>(__t);
}

2.auto和decltype操作符

C++中auto是根据用户初始化内容自动推导类型

  1. 使用auto时必须初始化
  2. auto变量不能作为自定义类型的成员变量
  3. auto不能声明数组
  4. 模板实例化类型不能是auto类型
  5. auto作为函数返回值类型时,需要额外再次指定返回类型,如:auto get(int a,double b)->int

decltype可以用来获取变量、函数、表达式的类型

追踪返回值类型:auto和decltype相结合

auto func(int a,double b)->decltype(a+b)
{
   return a+b;
}

在模板中使用更简洁

template
auto mul(T1 &t1,T2 &t2)->->decltype(t1*t2)
{
    return t1*t2;
}
转载请注明:文章转载自 www.wk8.com.cn
本文地址:https://www.wk8.com.cn/it/1036462.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 wk8.com.cn

ICP备案号:晋ICP备2021003244-6号