在C++中,为什么我可以将常量shared_ptr分配给非常量shared_ptr?

3

我之前认为不能将一个常量 shared_ptr 分配给非常量的 shared_ptr。但令人惊讶的是,我可以像下面这样分配,而且它能正常工作。

#include <iostream>
#include <memory>

using namespace std;

int main()
{
    const std::shared_ptr<const string> a = std::make_shared<const string>("Hello world");

    std::shared_ptr<const string> b = a;
    cout << "a count " << a.use_count() << ", b count " << b.use_count() << endl;
    return 0;
}

.use_count()被打印为2。请有人帮我理解我是如何做到的?


可能会发生什么不是预期的情况?这是否会破坏任何常量正确性? - Ulrich Eckhardt
1
你没有将const指针分配给非const指针。你复制了对象数据。指针在shared_ptr对象内部。 - RbMm
1
存在shared_ptr& operator=( const shared_ptr& r )shared_ptr( const shared_ptr<Y>& r ),它们明确允许b = a;的操作。 - RbMm
3个回答

10

你代码中的情况与这里完全相同:

const int a = 5;
int b = a;
std::cout << "a=" << a ", b=" << b << std::endl; // a=5, b=5
b = 10;
std::cout << "a=" << a ", b=" << b << std::endl; //a=5, b=10

并不是很出乎意料,对吧?我使用了const int来初始化非const的int。从a中获取的值被复制到了b中,而a根本没有被修改。

使用const std::shared_ptr时也是相同情况。复制构造另一个对象并不会修改原始对象。

use_count可以改变,因为它不是std::shared_ptr类的成员。 std::shared_ptr需要在堆上分配两个内存块-控制块和实际对象块。
每个std::shared_ptr实例仅存储指向控制块和实际对象的指针。控制块存储使用计数(保持指向它的std::shared_ptr数量)。

当您复制std::shared_ptr时,它会增加控制块中的使用计数并获得相同的两个指针。当std::shared_ptr消亡时,它会减少使用计数(如果使用计数达到0,则删除两个块)。

因此,总结一下:使用计数不是std::shared_ptr的成员,因此即使对于const std::shared_ptr,它也可以改变(否则const std::shared_ptr将会相当无用)。


1
由于指针是const类型,您可能需要指出原始对象的use_count如何被修改。 - NathanOliver
按照同样的逻辑,为什么我不能将const char *赋值给char *? - kadina
3
因为 const char *const shared_ptr<T> 不是同一种东西。在 const char * 中,const 作用于 char 而不是 *。若要达到相同的效果,需要使用 char * const,它可以被分配给一个 char * - NathanOliver
1
真的更接近示例(非常简单)- https://godbolt.org/z/SHe68r - RbMm
2
@kadina 因为const char *对应的是std::shared_ptr<const char>而非const std::shared_ptr<char> - Slava

7

ab所指向的字符串在两种情况下仍然是const,但是指针b不是,因此您可以更改b所指向的内容:

std::shared_ptr<const string> b = a;
b = std::make_shared<const string>("New string");

但是您不能更改 a 指向的内容(因为 aconst):

a = std::make_shared<const string>("Won't compile");

同样地:
const char* const a = "Hello world";
const char* b = a;

const char* c = "Something else";
b = c;    // the pointer "b" is not const and can be changed
// a = c; // won't compile since the pointer "a" itself is const

2
最初的回答:

让我们简化:

#include <iostream>
#include <memory>

int main() {
    const auto a = std::make_shared<const std::string>("Hello world");

    auto b = a;
    std::cout << "a count " << a.use_count() << ", b count " << b.use_count() << "\n";
}

可以从可变对象复制构造的类型,但不能从常量对象中进行复制构造的类型非常罕见且全部是用户定义的。大多数情况下,它们早于移动语义,因此适用于 C++11 之前的版本。
std::shared_ptr 是在 C++11 中引入的,不属于此类异常。为什么呢?

最初的回答:很少有允许从可变对象中进行复制构造而不允许从常量对象中进行复制构造的类型,这些类型大多是早于移动语义的,因此适用于 C++11 之前的版本。然而,C++11 中引入的 std::shared_ptr 并不属于这种类型。为什么呢?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接