36

Sean Parent 在 Going Native 2013 上发表了题为Inheritance Is The Base Class of Evil的演讲。在 20 分 50 秒后,他声明指向不可变 (const) 类型 ( std::shared_pointer<const T>) 的共享指针具有值语义。这到底是什么意思?为什么它与指向可变(非常量)类型()的共享指针有什么不同std::shared_pointer<T>

4

4 回答 4

49

不幸的是,就像Going Native 2013上的所有演讲一样,它受到非常紧迫的时间安排的限制。不过,对我们来说幸运的是,Sean Parent 去年在 C++Now 上做了一个更彻底的演讲,名为Value Semantics and Concepts-based Polymorphism。它涵盖了相同的材料,并且可能会回答您的问题。无论如何,我会尽力解释......

介绍

一个类型可以有两种类型的语义:

  • 价值语义。
  • 参考语义。(有时称为指针语义。)

关于两者有何不同以及何时一个比另一个更可取,可以继续阅读很多页。让我们简单地说,使用值类型的代码更容易推理。

也就是说,值类型的实例在任何时候都不会发生任何不可预知的事情——引用类型无法保证这一点,因为被引用的值在你的代码的其他持有引用的部分之间共享。

换句话说:引用类型的可预测性较差,因为它们可以被一段遥远的代码改变。例如,您调用的函数可能会更改从您下方引用的值。或者,更糟糕的是,如果涉及线程,则引用类型可能随时被另一个碰巧对引用的值进行操作的线程更改。出于这个原因,Sean Parent 声明在能够推理使用 a 的代码时,ashared_ptr与全局变量一样好。

综上所述,我们应该准备好回答手头的问题。


问题和答案

对于值类型T,为什么shared_ptr<const T>即使它是指针类型,它的行为也像值类型?

因为我们无法对const T所指向的内容进行更改,所以关于指针/引用类型难以预测的所有内容都不再适用。我们不再需要担心T被意外更改,因为它是一个 const 值类型。

如果我们确实想对 进行更改T,我们将不得不制作一份副本,让持有 的其他人shared_ptr<const T>不受我们行为的影响。此外,甚至可以使用一种称为Copy-on-write的机制将副本隐藏在值类型中,这似乎是 Sean Parent 最终所做的。


我想我已经像 Sean Parent 那样回答了这个问题(并且在链接的 C++Now 演示文稿中做了),但是让我们再进一步补充一点......

一个大附录:

(感谢@BretKuhns提出这个问题并在评论中提供了一个例子。)

这整个概念有一个令人讨厌的问题。shared_ptr<const T>除非我们知道对该实例的所有活动指针/引用都是 ,否则说它的行为类似于值类型不一定是T正确的const。这是因为const修饰符是单向的——持有 ashared_ptr<const T>可能会阻止我们修改 的实例T,但不会阻止其他人T通过指向非的指针/引用来修改const

知道了这一点,shared_ptr<const T>除非我知道所有指向它的活指针都是const. 但是,要知道这样的事情,需要对代码的所有用法都有全局了解shared_ptr<const T>——这对于值类型来说不是问题。出于这个原因,这样说可能更有意义:A shared_ptr<const T>can be used to support value semantics


顺便说一句,我实际上是在 2013 年的 Going Native 上——也许你可以在左前方看到我的后脑勺。

于 2013-09-14T16:02:47.097 回答
7

我举了3个例子。在所有三种情况下,我都a使用 content创建了一个变量"original value"b然后我通过说创建另一个变量,auto b = a;并在此语句之后分配a内容"new value"

如果a并且b具有值语义,我希望b的内容是"original content". 事实上,这正是发生在stringandshared_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();
}

输出:

--------------
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

话虽如此,我希望他有更多的时间:在那次演讲之后,我仍然想知道一些事情。我很相信这只是时间不够,他似乎是一位出色的主持人。

于 2013-09-13T21:17:53.953 回答
5

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

值语义的主要定义特征是具有相同内容的两个对象是相同的。整数是值类型:a 5 与任何其他 5 相同。将其与引用机制进行比较,其中对象具有标识。a包含 [1, 2] 的列表与包含 [1, 2] 的列表不同 ,b因为追加 3 与a追加 3 的效果不同b。的身份与的身份a不同。b

这往往是直观的……用词来表达听起来很奇怪。如果没有对值类型与引用类型有一些直观的了解,没有人会在 C++ 中完成 3 天。

如果你有一个可变值类型并且你想复制它,你必须实际复制对象的内容。这是昂贵的。

Sean 所指的技巧是,如果一个对象是不可变的,那么您不必复制整个对象,只需引用旧对象即可。这要快得多。

于 2013-09-10T04:20:19.833 回答
3

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

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

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

可能您将“不变性”误认为是含义const T-在您说它们相同的问题中,但事实并非如此。

于 2013-09-13T21:29:13.807 回答