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

时间:2013-09-10 02:00:01

标签: c++ c++11

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 个答案:

答案 0 :(得分:44)

不幸的是,与Going Native 2013的所有会谈一样,它受到时间紧迫的限制。幸运的是,对于我们来说,Sean Parent去年在C ++上做了一个更加全面的讨论,现在称为Value Semantics and Concepts-based Polymorphism。它涵盖相同的材料,可能会回答您的问题。无论如何我都会去解释....

简介

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

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

关于两者如何不同以及何时优于另一方,可以继续许多页面。让我们简单地说,使用值类型的代码可以更容易被推理。

也就是说,对于值类型的实例,任何时候都不会发生任何不可预测的事情 - 使用引用类型无法保证这一点,因为引用的值在代码的其他部分之间共享,这些部分包含引用它。

换句话说:引用类型的可预测性较低,因为它们可以通过远程代码进行更改。例如,您调用的函数可能会更改从您下面引用的值。或者,更糟糕的是,如果涉及线程,则可以随时通过对引用的值进行操作的另一个线程来更改引用类型。出于这个原因,Sean Parent在能够推断使用一个代码的代码时,声明 a shared_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修饰符是单行道 - 持有shared_ptr<const T>可能会阻止我们修改T的实例,但不会阻止< em>其他人通过指向非T的指针/引用来修改const

知道了这一点,我会厌倦地发表shared_ptr<const T>与值类型一样好的广泛陈述,除非我知道所有生命指针都是const。但是,知道这样的事情需要全局了解shared_ptr<const T>的所有用法代码 - 这对于值类型来说不是问题。出于这个原因,可能更有意义的说: A shared_ptr<const T>可以用来支持价值语义


在旁注中,我实际上是在2013年的Going Native中 - 也许你可以在左前方看到我的后脑勺。

答案 1 :(得分:7)

我举三个例子。在所有三种情况下,我都会创建一个内容为a的变量"original value"。然后我通过说b创建另一个变量auto b = a;,在此语句之后我将a内容"new value"分配给。{/ p>

如果ab具有值语义,我希望b的内容为"original content"。事实上,stringshared_ptr<const string>确实发生了这种情况。 auto b = a;的概念含义与这些类型相同。与shared_ptr<string>不同,b将包含内容"new value"

代码(online demo):

#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

话虽如此,我希望他有更多的时间:在演讲结束后,我仍然想知道一些事情。我相信只是缺乏时间,他似乎是一位优秀的主持人。

答案 2 :(得分:5)

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

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

这往往是直观的...当用文字说话时听起来很奇怪。没有人在C ++中花3天时间而没有直观的价值类型与参考类型相比。

如果您有 mutable 值类型并且想要复制它,则必须实际复制对象的内容。这很贵。

Sean所指的是,如果一个对象是不可变的,那么你不必复制整个对象,你可以只参考旧对象。这要快得多。

答案 3 :(得分:3)

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

当然,这不仅仅是原始const T*的存在构成了对象为const的证据。

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

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