https://www.zybuluo.com/guochy2012/note/14265
http://ewangplay.appspot.com/?p=27001
定义
值语义和引用语义的定义是这样的:复制后与以前的对象无关的对象叫做值语义,无法复制或者复制后与原来的对象存在关联的对象称为引用语义。
那么该如何理解上面的定义呢? 我的看法是:对象看起来表现的像一个值,就叫做值语义。
Java
我们先从Java中的相关语法看起:
int a = 8;int b = a;
这里的a b 都是int,属于java中原生数据类型,他们的特点是复制后二者再无关联。它们表现出来是两个value,所以int类型为值语义(value semantic)。
与他们形成鲜明对比的是用户自定义的class:
Employee a1 = new Employee("zhangsan", 23);Employee a2 = a1;a2.setAge(30);System.out.println(a1.getAge());
这段代码输出显然是30,第二行语句,导致a和b指向了同一个实际的对象。在这里,java的对象表现的行为不是一种value的行为,而是一种reference,所以这里称java中的对象为引用语义(reference semantic)。
C++
回到C++中,再看这个问题,以C++中一个普通的类为例:
class ItemBook{private:string isbn_;double price_;int amount_;};
如果我们写出以下的代码:
ItemBook a1;// set member of a1;ItemBook a2 = a1;
那么a1和a2复制后没有任何关联,他们的表现和java中的原生数据类型一样,属于value语义。事实上,C++内置的标准库类型例如string、vector、list等都是值语义,凡是可以放入容器的元素都必须具备value语义,即必须具备拷贝的能力,放入标准容器的元素和之前的元素没有任何的关联。
那么C++中的引用语义看起来有些奇怪,可以这样理解:正是因为类不可复制,所以我们可以采用智能指针,来把它们转化为reference语义。
典型的例子是TcpConnection,它显然是不可复制的(因为牵扯到系统的资源),那么我们在程序中该如何把它作为参数传递呢?我们定义下面的类型:
typedef boost::shared_ptr<TcpConnection> TcpConnectionPtr;
这样我们在使用TcpConnection的场合一概采用TcpConnectionPtr,它表现的像一个引用,这就实现了引用语义。
对于那些对象复制后存在关联的对象,很多时候我们干脆禁用掉copy能力。
Java和C++的本质区别
我们可以得出这样一个结论,C++和java的本质区别在于语义不同!
总结
在设计C++的类时,严格的区分开对象是否可复制,也就是严格区分value语义和reference语义,这样可以避免很多潜在的BUG。
=========
STL容器中的元素必须满足下面的几点基本需求:
- 容器中的元素必须是可复制的,而且复制实例跟原实例要保持一致。(通过拷贝构造函数)
- 容器中的元素必须是可赋值的。(通过赋值操作符)
- 容器中的元素必须是可销毁的。(通过析构函数)
- 对于序列容器中元素,默认构造函数必须是可用的。
- 对于关联容器中元素,诸如<,>等比较操作符必须提供,因为关联容器中的排序规则需要用到这些操作符。
- 一般情况下,==操作符也要提供,通常情况下查找元素操作需要用到。
(2)容器的值语义和引用语义
- 复制元素是简单容易操作的。
- 引用语义是容器出错的。你必须时刻保证所引用的对象没有被销毁,而且你还要提防循环引用问题。
- 复制复杂的对象将导致性能的下降。
- 在不同的容器中引用管理一个对象实例变得不可能。
智能指针 boost::shared_ptr 基本上类似于 boost::scoped_ptr。 关键不同之处在于 boost::shared_ptr 不一定要独占一个对象。 它可以和其他boost::shared_ptr 类型的智能指针共享所有权。 在这种情况下,当引用对象的最后一个智能指针销毁后,对象才会被释放。
因为所有权可以在 boost::shared_ptr 之间共享,任何一个共享指针都可以被复制,这跟 boost::scoped_ptr 是不同的。 这样就可以在标准容器里存储智能指针了——你不能在标准容器中存储 std::auto_ptr,因为它们在拷贝的时候传递了所有权。
#include <boost/shared_ptr.hpp>
#include <vector>
int main()
{
std::vector<boost::shared_ptr<int> > v;
v.push_back(boost::shared_ptr<int>(new int(1)));
v.push_back(boost::shared_ptr<int>(new int(2)));
}
多亏了有 boost::shared_ptr,我们才能像上例中展示的那样,在标准容器中安全的使用动态分配的对象。 因为 boost::shared_ptr 能够共享它所含对象的所有权,所以保存在容器中的拷贝(包括容器在需要时额外创建的拷贝)都是和原件相同的。如前所述,std::auto_ptr做不到这一点,所以绝对不应该在容器中保存它们。
浙公网安备 33010602011771号