#include <iostream>
using std::cout;
using std::endl;
class A
{
public:
A()
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;//链式编程
}
private:
//禁止复制:从语法层面去表达语义
A(const A &);
A& operator=(const A &);
};
|
如果一个对象能够进行复制,表达的是值语义
如果一个对象不能进行复制,表达的是对象语义
int main(void)
{
A a;//栈对象的创建需要析构函数
//A b = a;//Error,A类创建的对象不能进行复制
A c;
//c = a; //Error,A类创建的对象不能进行赋值
return 0;
}
|
【1】什么是值语义?
所谓值语义是指目标对象由源对象拷贝生成,且生成后与源对象完全无关,彼此独立存在,改变互不影响。就像 int 类型变量相互拷贝一样。
C++的内置类型(bool/int/double/char)都是值语义,标准库里的 complex<>、pair<>、vector<>、map<>、string 等等类型也都是值语义。
拷贝之后就与源对象完全脱离关系。
【2】什么是对象语义?
对象语义也叫指针语义,引用语义等。
通常是指一个目标对象由源对象拷贝生成,但生成后与源对象之间依然共享底层资源,对任何一个的改变都将随之改变另一个。
就像包含有指针成员变量的自定义类在默认拷贝构造函数下对其对象之间进行的拷贝。拷贝后目标对象和源对象的指针成员变量仍指向同一块内存数据。
如果当其中一个被析构掉后,另一个对象的指针成员就会沦为名副其实的悬垂指针!
又比如,Thread 是对象语义,拷贝 Thread 是无意义的,也是被禁止的:因为 Thread 代表线程,拷贝一个Thread对象并不能让系统增加一个一模一样的线程。
【3】两者之间的联系
“值” 与 “对象”类型之间并没有严格定义的区分。但通常可以观察到下列不同:
值 是 死的、 傻的、 简单的、 具体的、 可复制的
对象 是 活的、 聪明的、 复杂的、 抽象的、 不可复制的
这里的“复杂性”主要还是指行为的复杂性,而非结构的复杂性。例如,
list< map< vector<string>, deque< set<int> > > >
仍然是一个不折不扣的“值”类型。
值语义的一个巨大好处是生命期管理很简单,比如 int 类型一样——你不需要操心 int 对象 的生命期。
值语义的对象要么是栈对象,或者直接作为其它对象的数据成员,因此我们不用担心它的生命期(一个函数使用自己栈上的对象,一个成员函数使用自己的数据成员变量)。
相反,对象语义的 对象由于不能拷贝,我们只能通过指针或引用来使用它。
一旦使用指针和引用来操作对象,那么就要担心所指的对象是否已被释放,这一度是C++程序bug的一大来源。
此外,由于C++只能通过指针或引用来获得多态性,那么在C++里从事基于继承和多态的面向对象编程有其本质的困难——内存资源管理。
值语义是指对象的拷贝与原对象无关。拷贝之后就与原对象脱离关系,彼此独立互不影响。比如说int,C++中的内置类型都是值语义,我们前面学过的三个标准库类型string,vector,map也是值语义
|
对象语义指的是面向对象意义下的对象
对象拷贝是禁止的(Noncopyable)。
一个对象被系统标准的复制方式复制后,与被复制的对象之间依然共享底层资源,对任何一个的改变都将改变另一个
|
值语义对象生命期容易控制
|
对象语义对象生命不容易控制(通过智能指针来解决)。智能指针实际上是将对象语义转化为值语义
|
值语义与对象语义是分析模型决定的,语言的语法技巧用来匹配模型。
|