c++的三种变量语义

Posted zkccpro

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++的三种变量语义相关的知识,希望对你有一定的参考价值。

c++的三种变量语义

1. 值语义变量

这。。。其实我们大一学c语言的时候都知道它是啥!但当时的自己可能并不理解值语义变量到底意味着啥???直到我反复研读primer、mordern c++。再结合自己写代码,慢慢才对这个抽象有了更深的认识。

值语义变量,比如:

int a=0;

std::vector<int> arr;

class A
    //some functions...
	Big* big_;//这是一个占用很大空间的类
;
A aa;

看似这很简洁,就是不带引用和指针修饰的变量嘛!他们的存储空间开辟在栈上,声明周期由栈管理,不用担心内存泄漏什么的问题。这听起来很好,但有一个最大的问题:拷贝

//承接上面的代码
std::vector<int> arr1=arr;
A bb(aa);

这会导致什么呢?没错,就是深拷贝的代价。以上两句分别触发了c=和c-ctor。为了保证释放指针的安全性,拷贝构造将会使用深拷贝的写法。(std::shared_ptr是一种避免深拷的方法,但shared本身也会带来性能损耗呀!)如果Big类对象、arr很大的话,深拷显然很不划算。。这一点在函数实参形参传递时很常见!(返回值会有RVO优化一般不用担心性能问题。)

2. 对象语义

对象这个概念严格来说应该是java里的吧。。对象语义的出现就是为了防止深拷贝带来性能损耗的。原理也很简单,用两个指针指向一片空间,共用之就行了。当然问题也是有的,那就是释放的时候要记着只能释放它们其中一个。。因此,c++陆续推出了引用、智能指针来包装对象语义。

int a=0;
int& ra=a;
int* pa=&a;

引用和指针都算作对象语义的实现。和java的对象语义不同,c++并不是严格地规定对象的空间必须在堆上。对象指向栈上或者其他什么地方也未尝不可。(只要你能玩明白就行。。。)

引用和智能指针分别有啥优点这里就不展开啦,之前讨论过了。这里的重点是,对象语义可以避免深拷带来的性能损耗。

3. 右值语义

右值语义是c++11之后新推出的一种语义,和它紧密联系的就是移动语义了。(但移动语义不是变量语义昂,是一种行为语义)和右值对应的就是左值,左值语义一般来说包括以上两种:值变量与对象变量。

右值语义的定义是很自然的:想象一下,以上两种变量的生命周期都是较长一段时间的——不论在栈上或是堆上。那在高级语言中该不该有一种变量的生命只存在于一个语句呢(暂时的)?肯定是有的啊!比如:

int a=2;//这句中的“2”,在汇编里叫“立即数”吧!

class A
    //something...
    static void static_func();
;
A().static_func();//c++中的临时变量:“A()”

int&& rra=std::move(a);//c++11中,我们可以自己“构造”一个右值变量了

在语句:int&& rra=std::move(a);中,a是std::move(a)这个表达式被认为是一个右值(即返回值是右值),但rra却是一个左值,被称为右值引用。这一点必须在本节搞清楚!所以说,右值引用的作用也很简单,就是可以”接住“转瞬即逝的右值,将快死掉的右值附到一个持久的左值上,留作后用。这个左值就是右值引用。

是不是很清晰呢?这些这些知识在通读primer之后就可以搞清楚了,算是很基本的用法。但下一节要讲的std::move和std::forward,貌似就没这么容易懂了!(反正我看了primer是没搞明白)想搞清楚这二者的区别,本节的内容是基础,必须彻底明白。

以上是关于c++的三种变量语义的主要内容,如果未能解决你的问题,请参考以下文章

C++ 中string类的三种模拟实现方式

Hibernate对象的三种状态

一文精通kafka 消费者的三种语义

一文精通kafka 消费者的三种语义

C++ 动态内存开辟

C++ 动态内存开辟