指向不可变types的共享指针具有值语义

肖恩家长在“2013年原住民”杂志上发表了题为“ inheritance是邪恶基础类 ”的演讲。 在20分钟50秒的时间内,他发表了一个声明:一个指向不可变(const)types( std::shared_pointer<const T> )的共享指针具有值语义。 这到底是什么意思? 为什么它与共同指向可变(非const)types( std::shared_pointer<T> )的指针有什么不同?

不幸的是,像“2013年原住民”的所有谈话一样,它受到时间紧迫的限制。 对我们来说幸运的是,Sean Parent去年在C ++ Now上进行了一次更彻底的讨论,名为Value Semantics and Concepts-based Polymorphism 。 它涵盖了相同的材料,可能会回答你的问题。 无论如何,我会解释一下。

介绍

一种types可以有两种types的语义:

  • 价值语义。
  • 引用语义。 (有时称为指针语义。)

关于两者如何不同以及什么时候一方比另一方更可取,可能会有很多页面。 让我们简单地说, 使用值types的代码可以更容易推理。

也就是说,在某个值types的实例的任何一点上都不会发生不可预知的事情,这些引用types无法保证,因为所引用的值是在代码的其他部分之间共享的,这些部分对引用进行引用。

换句话说:引用types的可预测性较差,因为它们可以被一段遥远的代码所改变。 例如,您调用的函数可能会更改您所引用的值。 或者更糟糕的是,如果涉及到线程,那么引用types可以随时由另一个线程改变,这个线程恰好在引用的值上运行。 由于这个原因,Sean Parent声明一个shared_ptr和全局variables一样好可以shared_ptr出使用它的代码。

有了这些,我们应该准备好回答这个问题。


问题和答案

对于一个值typesT ,为什么shared_ptr<const T>尽pipe是一个指针types就像一个值types?

因为我们不能对指向的const T进行更改,所以关于指针/引用types的所有内容都不太可预测。 我们再也不用担心T被意外的改变,因为它是一个常量值types。

如果我们想要对T进行更改,我们必须复制它,留下持有shared_ptr<const T>其他人不受我们的行为影响。 而且,拷贝甚至可以使用名为Copy-on-write的机制隐藏在值types中,这似乎是Sean Parent最终做的。


我想我已经回答了Sean Parent的问题(并且在链接的C ++ Now演示文稿中),但是让我们进一步阅读附录…..

大附录:

(感谢@BretKuhns提出这个build议,并在评论中提供一个例子。)

这整个概念有一个唠叨的事情是错误的。 说shared_ptr<const T>行为像一个值types,除非我们知道T所有活动指针/引用都是const ,否则不是非常正确的。 这是因为const修饰符是单向街道 – 持有shared_ptr<const T>可能会阻止我们修改T的实例,但不会阻止其他人通过指向非const的指针/引用来修改T

知道这一点,我会厌倦的做广泛的声明, shared_ptr<const T>是一样好的价值types,除非我知道所有活的指针是const 。 但是,知道这样的事情需要全面了解shared_ptr<const T>所有用法的代码 – 这对于一个值types来说不是问题。 出于这个原因,可能更有意义的说:类似于shared_ptr<const T>可以用来支持值语义


在一个侧面说明中,我实际上是2013年的原住民 – 也许你可以在左前方看到我的头。

我给3个例子。 在所有三种情况下,我创build了a内容为"original value"的variablesa 。 然后我通过说auto b = a;创build另一个variablesb auto b = a; 并在此声明之后,我分配的内容"new value"

如果ab有价值语义,我希望b的内容是"original content" 。 事实上,这恰好发生在stringshared_ptr<const string>auto b = a;的概念含义auto b = a; 与这些types是一样的。 与shared_ptr<string>没有多less关系, b将会有内容"new value"

代码( 在线演示 ):

 #include <iostream> #include <memory> #include <string> using namespace std; void string_example() { auto a = string("original value"); auto b = a; // true copy by copying the value a = string("new value"); cout << "a = " << a << endl; cout << "b = " << b << endl; cout << boolalpha << "&a == &b ? " << (&a==&b) << endl; } void shared_ptr_example() { auto a = make_shared<string>("original value"); auto b = a; // not a copy, just and alias *a = string("new value"); // and this gonna hurt b cout << "a = " << *a << endl; cout << "b = " << *b << endl; cout << boolalpha << "&a == &b ? " << (&a==&b) << endl; } void shared_ptr_to_const_example() { auto a = make_shared<const string>("original value"); auto b = a; //*a = string("new value"); // <-- now won't compile a = make_shared<const string>("new value"); cout << "a = " << *a << endl; cout << "b = " << *b << endl; cout << boolalpha << "&a == &b ? " << (&a==&b) << endl; } int main() { cout << "--------------" << endl; cout << "string example" << endl; string_example(); cout << "------------------" << endl; cout << "shared_ptr example" << endl; shared_ptr_example(); cout << "---------------------------" << endl; cout << "shared_ptr to const example" << endl; shared_ptr_to_const_example(); } 

输出:

 -------------- string example a = new value b = original value &a == &b ? false ------------------ shared_ptr example a = new value b = new value &a == &b ? false --------------------------- shared_ptr to const example a = new value b = original value &a == &b ? false 

话虽如此,但我希望他有更多的时间:在演讲结束后,我仍然有一些想法。 我相当确信这只是缺乏时间,他似乎是一个出色的主持人。

他的意思是他们可以用来模拟价值语义

价值语义的主要定义特征是具有相同内容的两个对象是相同的。 整数是值types:5与其他5相同5.将其与对象具有标识的引用机制进行比较 包含[1,2]的列表b包含[1,2]的列表b不一样,因为向a追加3与追加bb作用并不相同。 a的身份不同于b身份

这往往是直观的…当听到这些话时听起来很奇怪。 没有人在C ++中使用3天,而没有获得值types与参考types的直观感觉。

如果你有一个可变的值types,你想复制它,你必须实际复制对象的内容。 这很贵。

Sean指的是,如果一个对象是不可变的,那么你不必复制整个对象,你可以引用旧对象。 这是更快。

他似乎假定shared_ptr<const T>的存在意味着该对象的所有句柄也是shared_ptr<const T> (也就是说,只读)。

当然,这不是比原始const T*的存在更真实的const T*构成对象是constcertificate。

演示: http : //ideone.com/UuHsEj

可能你错误​​地认为const T “不变性” – 在你说的问题上他们是一样的,但事实并非如此。