CPP值和对象

值语义

对象的拷贝与原对象无关。拷贝之后就与原对象脱离关系,彼此独立互不影响(深拷贝)。比如说int,C++中的内置类型都是值语义,标准库类型string、vector、map等等也是值语义。

对象语义

对象拷贝是禁止的(Noncopyable) 或者一个对象被系统标准的复制方式复制后,与被复制的对象之间依然共享底层资源,对任何一个的改变都将改变另一个(浅拷贝)。

QT中对象的定义

Qt Objects: Identity vs Value
Some of the added features listed above for the Qt Object Model, require that we think of Qt Objects as identities, not values. Values are copied or assigned; identities are cloned. Cloning means to create a new identity, not an exact copy of the old one. For example, twins have different identities. They may look identical, but they have different names, different locations, and may have completely different social networks.
Then cloning an identity is a more complex operation than copying or assigning a value. We can see what this means in the Qt Object Model.
A Qt Object…

  1. might have a unique QObject::objectName(). If we copy a Qt Object, what name should we give the copy?
  2. has a location in an object hierarchy. If we copy a Qt Object, where should the copy be located?
    can be connected to other Qt Objects to emit signals to them or to receive signals emitted by them.
  3. If we copy a Qt Object, how should we transfer these connections to the copy?
  4. can have new properties added to it at runtime that are not declared in the C++ class. If we copy a Qt Object, should the copy include the properties that were added to the original?

For these reasons, Qt Objects should be treated as identities, not as values. Identities are cloned, not copied or assigned, and cloning an identity is a more complex operation than copying or assigning a value. Therefore, QObject and all subclasses of QObject (direct or indirect) have their copy constructor and assignment operator disabled.

机翻

Qt 对象:身份与价值
上面为 Qt 对象模型列出的一些附加功能要求我们将 Qt 对象视为身份,而不是值。值被复制或分配;身份被克隆。克隆意味着创建一个新身份,而不是旧身份的精确副本。例如,双胞胎有不同的身份。它们可能看起来相同,但名称不同,位置不同,并且可能拥有完全不同的社交网络。
那么克隆身份是比复制或分配值更复杂的操作。我们可以在 Qt 对象模型中看到这意味着什么。
一个 Qt 对象…

  1. 可能有一个唯一的QObject::objectName()。如果我们复制一个 Qt 对象,我们应该给副本起什么名字?
  2. 在对象层次结构中有一个位置。如果我们复制一个 Qt 对象,该副本应该位于何处?
    可以连接到其他 Qt 对象以向它们发出信号或接收它们发出的信号。
  3. 如果我们复制一个Qt Object,我们应该如何将这些连接转移到副本中?
  4. 可以在运行时添加未在 C++ 类中声明的新属性。如果我们复制一个 Qt 对象,该副本是否应该包括添加到原始对象的属性?

由于这些原因,Qt 对象应该被视为身份,而不是值。身份是克隆的,而不是复制或分配的,克隆身份是比复制或分配值更复杂的操作。因此,QObjectQObject 的所有子类(直接或间接)都禁用了它们的复制构造函数和赋值运算符。

内存管理

  1. 为了简化管理内存,自定义的类,统一禁用给定类的拷贝构造函数和赋值运算符的使用(作为对象语义使用)。
  2. 当然如果必要使用值语义,就必须自己实现拷贝构造函数和赋值运算符重载。不然,如果拷贝只是浅拷贝,共享某一个指针对象等一些特殊情况,很容易发生内存多次释放或者其他由于内存引发的BUG,还有同一个文件描述符在一个实体内部关闭,造成其他实体使用失效等等问题。
  3. 当然无论是值语义还是对象语义,都会涉及引用或者指针访问对象,为了保证指针的有效性(生命周期),彻底规避此类内存问题,需要使用智能指针进行包装访问。

对象和值语义类的基类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class NonCopyable
{
public:
NonCopyable(const NonCopyable &) = delete;
void operator=(const NonCopyable &) = delete;

protected:
NonCopyable() = default;
~NonCopyable() = default;
};

class Copyable
{
protected:
Copyable() = default;
~Copyable() = default;
};