46

I have a few containers in a class, for example, vector or map which contain shared_ptr's to objects living on the heap.

For example

template <typename T>
class MyExample
{
public:

private:
    vector<shared_ptr<T> > vec_;
    map<shared_ptr<T>, int> map_;
};

I want to have a public interface of this class that sometimes returns shared_ptrs to const objects (via shared_ptr<const T>) and sometimes shared_ptr<T> where I allow the caller to mutate the objects.

I want logical const correctness, so if I mark a method as const, it cannot change the objects on the heap.

Questions:

1) I am confused by the interchangeability of shared_ptr<const T> and shared_ptr<T>. When someone passes a shared_ptr<const T> into the class, do I:

  • Store it as a shared_ptr<T> or shared_ptr<const T> inside the container?
  • OR
  • Do I change the map, vector types (e.g. insert_element(shared_ptr<const T> obj)?

2) Is it better to instantiate classes as follows: MyExample<const int>? That seems unduly restrictive, because I can never return a shared_ptr<int>?

4

5 回答 5

39

shared_ptr<T>并且shared_ptr<const T>不可互换。它是一种方式 -shared_ptr<T>可以转换shared_ptr<const T>但不能反过来。

观察:

// f.cpp

#include <memory>

int main()
{
    using namespace std;

    shared_ptr<int> pint(new int(4)); // normal shared_ptr
    shared_ptr<const int> pcint = pint; // shared_ptr<const T> from shared_ptr<T>
    shared_ptr<int> pint2 = pcint; // error! comment out to compile
}

编译通过

cl /EHsc f.cpp

您还可以基于常量重载函数。你可以结合做这两个事实来做你想做的事。

至于你的第二个问题,MyExample<int>可能比MyExample<const int>.

于 2010-01-17T03:16:58.490 回答
13

我建议采用以下方法:

template <typename T>
class MyExample
{
  private:
    vector<shared_ptr<T> > data;

  public:
    shared_ptr<const T> get(int idx) const
    {
      return data[idx];
    }
    shared_ptr<T> get(int idx)
    {
      return data[idx];
    }
    void add(shared_ptr<T> value)
    {
      data.push_back(value);
    }
};

这确保了 const 正确性。就像您看到的那样,add() 方法不使用 <const T> 而是使用 <T>,因为您希望该类存储 Ts 而不是 const Ts。但是当访问它 const 时,您返回 <const T> 这没有问题,因为 shared_ptr<T> 可以很容易地转换为 shared_ptr<const T>。并且由于这两个 get() 方法都在您的内部存储中返回 shared_ptr 的副本,因此调用者不会意外更改您的内部指针指向的对象。这都可以与非智能指针变体相媲美:

template <typename T>
class MyExamplePtr
{
  private:
    vector<T *> data;

  public:
    const T *get(int idx) const
    {
      return data[idx];
    }
    T *get(int idx)
    {
      return data[idx];
    }
    void add(T *value)
    {
      data.push_back(value);
    }
};
于 2010-01-17T12:42:17.063 回答
6

如果有人通过你 ashared_ptr<const T>你应该永远无法修改T. 当然,在技术上可以将const T转换为T,但这破坏了制作T const. 因此,如果您希望人们能够将对象添加到您的课程中,他们应该给您shared_ptr<T>而不是shared_ptr<const T>. 当您从班级返回不想修改的内容时,即使用shared_ptr<const T>.

shared_ptr<T>可以自动转换(没有显式转换)为 ashared_ptr<const T>但不能反过来。它可以帮助你(无论如何你都应该这样做)自由地使用const方法。当您定义一个类方法const时,编译器将不允许您修改任何数据成员或返回除const T. 因此,使用这些方法将帮助您确保您没有忘记某些内容,并将帮助您的类的用户理解该方法的意图是什么。(例如virtual shared_ptr<const T> myGetSharedPtr(int index) const;:)

您的第二个陈述是正确的,您可能不想将您的类实例化为<const T>,因为您将永远无法修改您的任何Ts.

于 2010-01-17T02:56:36.020 回答
3

one thing to realize is that:

tr1::shared_ptr<const T> is mimicking the functionality of T const * namely what it points to is const, but the pointer itself isn't.

So you can assign a new value to your shared pointer, but I would expect that you wouldn't be able to use the dereferenced shared_ptr as an l-value.

于 2010-01-17T02:27:21.350 回答
0

序幕

限定符改变的const行为std::shared_ptr,就像它影响遗留的 C 指针一样。

应该始终使用正确的限定符来管理和存储智能指针,以防止、强制和帮助程序员正确对待它们。

答案

  1. 当有人将 a 传递给shared_ptr<const T>类时,我是将其存储为 ashared_ptr<T>还是shared_ptr<const T>在矢量和地图中,或者我是否更改地图、矢量类型?

如果您的 API 接受shared_ptr<const T>,则调用者和您之间的不言而喻的约定是不允许更改T指针指向的对象,因此,您必须将其保留在内部容器中,例如std::vector<std::shared_ptr<const T>>.

此外,您的模块永远不能/允许返回std::shared_ptr<T>,即使可以通过编程方式实现这一点(请参阅我对第二个问题的回答以了解如何)。

  1. 如下实例化类是否更好MyExample<const int>:这似乎过于严格,因为我永远无法返回shared_ptr<int>?

这取决于:

  • 如果您设计了模块,以便传递给它的对象将来不会再次更改,请const T用作基础类型。

  • 如果你的模块应该能够返回非常量T指针,你应该使用T你的底层类型并且可能有两个不同的getter,一个返回可变对象(std::shared_ptr<T>),另一个返回非可变对象(std::shared_ptr<const T>)。

而且,即使我希望我们刚刚同意,如果您有or ,您不应该返回,您可以std::shared_ptr<T>const Tstd::shared_ptr<const T>

const T a = 10;
auto a_ptr = std::make_shared<T>(const_cast<T>(a));

auto b_const_ptr = std::make_shared<const T>();
auto b_ptr = std::const_pointer_cast<T>(b_const_ptr);

完整的例子

const考虑以下示例,该示例涵盖了with的所有可能排列std::shared_ptr

struct Obj
{
  int val = 0;
};

int main()
{
    // Type #1:
    // ------------
    // Create non-const pointer to non-const object
    std::shared_ptr<Obj> ptr1 = std::make_shared<Obj>();
    // We can change the underlying object inside the pointer
    ptr1->val = 1;
    // We can change the pointer object
    ptr1 = nullptr;

    // Type #2:
    // ------------
    // Create non-const pointer to const object
    std::shared_ptr<const Obj> ptr2 = std::make_shared<const Obj>();
    // We cannot change the underlying object inside the pointer
    ptr2->val = 3; // <-- ERROR
    // We can change the pointer object
    ptr2 = nullptr;

    // Type #3:
    // ------------
    // Create const pointer to non-const object
    const std::shared_ptr<Obj> ptr3 = std::make_shared<Obj>();
    // We can change the underlying object inside the pointer
    ptr3->val = 3;
    // We can change the pointer object
    ptr3 = nullptr; // <-- ERROR

    // Type #4:
    // ------------
    // Create const pointer to non-const object
    const std::shared_ptr<const Obj> ptr4 = std::make_shared<const Obj>();
    // We can change the underlying object inside the pointer
    ptr4->val = 4; // <-- ERROR
    // We can change the pointer object
    ptr4 = nullptr; // <-- ERROR

    // Assignments:
    // ------------
    // Conversions between objects
    // We cannot assign to ptr3 and ptr4, because they are const
    ptr1 = ptr4 // <-- ERROR, cannot convert 'const Obj' to 'Obj'
    ptr1 = ptr3;
    ptr1 = ptr2 // <-- ERROR, cannot convert 'const Obj' to 'Obj'

    ptr2 = ptr4;
    ptr2 = ptr3;
    ptr2 = ptr1;
}

注意:在管理所有类型的智能指针时,以下情况是正确的。指针的分配可能不同(例如在处理时unique_ptr),但概念相同。

于 2019-08-07T14:06:01.230 回答