Sean Parent在Going Native 2013上发表了题为" 继承是邪恶的基础 "的演讲.在20分50秒时,他声明一个指向不可变(const)类型(std::shared_pointer<const T>
)的共享指针具有值语义.这到底是什么意思?为什么它与可变(非常量)类型(std::shared_pointer<T>
)的共享指针有什么不同?
Sea*_*ine 47
不幸的是,就像2013年Going Native的所有谈话一样,它受到时间紧迫的限制.幸运的是,对于我们来说,Sean Parent去年在C++上做了更全面的讨论,现在称为Value Semantics和基于概念的多态.它涵盖相同的材料,可能会回答您的问题.无论如何,我会先解释一下......
类型可以有两种类型的语义:
关于两者如何不同以及哪一个优于另一个,可以继续许多页面.让我们简单地说使用值类型的代码可以更容易地被推理.
也就是说,在值类型的实例的任何位置都不会发生任何不可预测的事情 - 使用引用类型无法保证这一点,因为引用的值在代码的其他部分之间共享,这些部分包含对它的引用.
换句话说:引用类型不太可预测,因为它们可以通过远程代码进行更改.例如,您调用的函数可能会更改从您下面引用的值.或者,更糟糕的是,如果涉及线程,则可以随时通过对引用的值进行操作的另一个线程来更改引用类型.出于这个原因,Sean Parent 在能够推断使用一个代码的代码时,声明a shared_ptr
和全局变量一样好.
尽管如此,我们应该准备好回答手头的问题.
对于值类型T
,为什么shared_ptr<const T>
即使它是指针类型,它也像值类型一样?
因为我们无法对const T
指向的内容进行更改,所以关于指针/引用类型的可预测性不再适用.我们不再需要担心T
被意外更改,因为它是一个const值类型.
如果我们不希望做出改变的T
,我们将不得不作出的一个副本,让其他人持有shared_ptr<const T>
我们的行动的影响.此外,复制甚至可以使用名为机制值类型藏在里面写入时复制,这似乎是什么肖恩家长最终做到了.
我想我已经回答了Sean Parent会提出的问题(并且在链接的C++ Now演示文稿中做过),但是让我们进一步了解附录.....
(感谢@BretKuhns提出的建议并在评论中提供了一个例子.)
这整个概念有一个令人烦恼的问题.shared_ptr<const T>
除非我们知道所有生命指针/对该实例的引用都是正确的,否则表示行为类似于值类型并不是必需T
的const
.这是因为const
修饰符是单行道 - 持有shared_ptr<const T>
可能会阻止我们修改实例T
,但不会阻止其他人T
通过指针/引用修改非const
.
知道了这一点,我会厌倦制作shared_ptr<const T>
一个与价值类型一样好的广泛陈述,除非我知道所有生活指针都是如此const
.但是,知道这样的事情需要围绕所有用法的代码的全局知识shared_ptr<const T>
- 这对于值类型来说不是问题.出于这个原因,可能更有意义的说:A shared_ptr<const T>
可以用来支持价值语义.
在旁注中,我实际上是在2013年的Going Native - 也许你可以在左前方看到我的后脑勺.
我举三个例子.在所有三种情况下,我都a
使用内容创建变量"original value"
.然后我b
通过说明auto b = a;
并在此语句之后创建另一个变量来分配a
内容"new value"
.
如果a
并且b
有价值语义,我希望它b
的内容是"original content"
.事实上,这恰好发生在string
和shared_ptr<const string>
.概念含义auto b = a;
与这些类型相同.没有那么多shared_ptr<string>
,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();
}
Run Code Online (Sandbox Code Playgroud)
输出:
--------------
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
Run Code Online (Sandbox Code Playgroud)
话虽如此,我希望他有更多的时间:在演讲结束后,我仍然想知道一些事情.我相信只是缺乏时间,他似乎是一位出色的主持人.
他的意思是它们可以用来模拟价值语义.
值语义的主要定义特征是具有相同内容的两个对象是相同的.整数是值类型:a 5与任何其他类型相同5.将其与参考机制进行比较,其中对象具有标识. 列表a
含有[1,2]是不一样的列表b
含有[1,2],因为追加3至a
不具有作为附加3相同的效果b
.该标识的a
比不同身份的b
.
这往往是直观的...当用文字说话时听起来很奇怪.如果没有直观的价值类型与参考类型相比,没有人能在C++中使用3天.
如果您有一个可变值类型并且想要复制它,则必须实际复制该对象的内容.这很贵.
Sean所指的是,如果一个对象是不可变的,那么你不必复制整个对象,你只需要引用旧对象即可.这要快得多.