指向不可变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"
。
如果a
和b
有价值语义,我希望b
的内容是"original content"
。 事实上,这恰好发生在string
和shared_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与追加b
到b
作用并不相同。 a的身份不同于b
的身份 。
这往往是直观的…当听到这些话时听起来很奇怪。 没有人在C ++中使用3天,而没有获得值types与参考types的直观感觉。
如果你有一个可变的值types,你想复制它,你必须实际复制对象的内容。 这很贵。
Sean指的是,如果一个对象是不可变的,那么你不必复制整个对象,你可以引用旧对象。 这是更快。
他似乎假定shared_ptr<const T>
的存在意味着该对象的所有句柄也是shared_ptr<const T>
(也就是说,只读)。
当然,这不是比原始const T*
的存在更真实的const T*
构成对象是const
certificate。
演示: http : //ideone.com/UuHsEj
可能你错误地认为const T
“不变性” – 在你说的问题上他们是一样的,但事实并非如此。