C++ 智能指针常量正确性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2079750/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C++ smart pointer const correctness
提问by user231536
I have a few containers in a class, for example, vector or map which contain shared_ptr's to objects living on the heap.
我在一个类中有几个容器,例如,vector 或 map,它们包含 shared_ptr 到位于堆上的对象。
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.
我想要这个类的公共接口,它有时将 shared_ptrs 返回给 const 对象(通过shared_ptr<const T>
),有时shared_ptr<T>
我允许调用者改变对象。
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:
1) 我对shared_ptr<const T>
和的互换性感到困惑shared_ptr<T>
。当有人通过 ashared_ptr<const T>
进入课堂时,我是否:
- Store it as a
shared_ptr<T>
orshared_ptr<const T>
inside the container? - OR
- Do I change the map, vector types (e.g. insert_element(
shared_ptr<const T>
obj)?
- 将其存储在容器中
shared_ptr<T>
或shared_ptr<const T>
内部? - 或者
- 我是否更改地图、向量类型(例如 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>
?
2)是否有更好的实例化类如下:MyExample<const int>
?这似乎过于严格,因为我永远无法返回shared_ptr<int>
?
回答by Terry Mahaffey
shared_ptr<T>
and shared_ptr<const T>
are notinterchangable. It goes one way - shared_ptr<T>
is convertable to shared_ptr<const T>
but not the reverse.
shared_ptr<T>
和shared_ptr<const T>
是不是可以互换的。它有一种方式 -shared_ptr<T>
可以转换为shared_ptr<const T>
但不能相反。
Observe:
观察:
// 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
}
compile via
编译通过
cl /EHsc f.cpp
cl /EHsc f.cpp
You can also overload a function based on a constness. You can combine to do these two facts to do what you want.
您还可以基于常量重载函数。你可以结合做这两个事实来做你想做的。
As for your second question, MyExample<int>
probably makes more sense than MyExample<const int>
.
至于你的第二个问题,MyExample<int>
可能比MyExample<const int>
.
回答by mmmmmmmm
I would suggest the following methotology:
我建议以下方法论:
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);
}
};
This ensures const-correctness. Like you see the add() method does not use <const T> but <T> because you intend the class to store Ts not const Ts. But when accessing it const, you return <const T> which is no problem since shared_ptr<T> can easily be converted to shared_ptr<const T>. And sice both get() methods return copies of the shared_ptr's in your internal storage the caller can not accidentally change the object your internal pointers point to. This is all comparable to the non-smart pointer variant:
这确保了常量正确性。就像您看到的 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);
}
};
回答by SoapBox
If someone passes you a shared_ptr<const T>
you should never be able to modify T
. It is, of course, technically possible to cast the const T
to just a T
, but that breaks the intent of making the T
const
. So if you want people to be able to add objects to your class, they should be giving you shared_ptr<T>
and no shared_ptr<const T>
. When you return things from your class you do not want modified, that is when you use shared_ptr<const T>
.
如果有人通过了shared_ptr<const T>
你,你应该永远无法修改T
. 当然的,技术上可以投的const T
只是一个T
,但断裂的意图作出的T
const
。因此,如果您希望人们能够向您的类添加对象,他们应该给您shared_ptr<T>
而不是shared_ptr<const T>
. 当您从类中返回不想修改的内容时,即使用shared_ptr<const T>
.
shared_ptr<T>
can be automatically converted (without an explicit cast) to a shared_ptr<const T>
but not the other way around. It may help you (and you should do it anyway) to make liberal use of const
methods. When you define a class method const
, the compiler will not let you modify any of your data members or return anything except a const T
. So using these methods will help you make sure you didn't forget something, and will help users of your class understand what the intent of the method is. (Example: virtual shared_ptr<const T> myGetSharedPtr(int index) const;
)
shared_ptr<T>
可以自动转换(没有显式转换)到 ashared_ptr<const T>
但不能反过来。自由地使用const
方法可能会帮助您(无论如何您都应该这样做)。当您定义类方法时const
,编译器不会让您修改任何数据成员或返回除 a 之外的任何内容const T
。因此,使用这些方法将帮助您确保您没有忘记某些东西,并帮助您的类的用户了解该方法的意图是什么。(实施例:virtual shared_ptr<const T> myGetSharedPtr(int index) const;
)
You are correct on your second statement, you probably do not want to instantiate your class as <const T>
, since you will never be able to modify any of your T
s.
您的第二条语句是正确的,您可能不想将类实例化为<const T>
,因为您将永远无法修改任何T
s。
回答by Evan Teran
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.
tr1::shared_ptr<const T>
正在模仿T const *
它指向的功能是const,但指针本身不是。
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.
因此,您可以为共享指针分配一个新值,但我希望您不能将取消引用shared_ptr
用作左值。
回答by Daniel Trugman
Prologue
序幕
The const
qualifier changes the behaviour of std::shared_ptr
, just like it affects the legacy C pointers.
在const
预选赛改变的行为std::shared_ptr
,就像它会影响传统的C指针。
Smart pointers should be managed and stored using the right qualifiers at all times to prevent, enforce and help programmers to treat them rightfully.
应该始终使用正确的限定符来管理和存储智能指针,以防止、强制执行和帮助程序员正确对待它们。
Answers
答案
- When someone passes a
shared_ptr<const T>
into the class, do I store it as ashared_ptr<T>
orshared_ptr<const T>
inside the vector and map or do I change the map, vector types?
- 当有人将 a 传递给
shared_ptr<const T>
类时,我是将它存储为 ashared_ptr<T>
还是shared_ptr<const T>
在矢量和地图中,还是更改地图、矢量类型?
If your API accepts a shared_ptr<const T>
, the unspoken contract between the caller and yourself is that you are NOT allowed to change the T
object pointed by the pointer, thus, you have to keep it as such in your internal containers, e.g. std::vector<std::shared_ptr<const T>>
.
如果您的 API 接受shared_ptr<const T>
,则调用者与您之间的不成文约定是不允许您更改T
指针指向的对象,因此,您必须将其保持在内部容器中,例如std::vector<std::shared_ptr<const T>>
.
Moreover, your module should NEVER be able/allowed to return std::shared_ptr<T>
, even though one can programatically achieve this (See my answer to the second question to see how).
此外,您的模块永远不能/允许返回std::shared_ptr<T>
,即使可以通过编程实现这一点(请参阅我对第二个问题的回答以了解如何)。
- Is it better to instantiate classes as follows:
MyExample<const int>
? That seems unduly restrictive, because I can never return ashared_ptr<int>
?
- 它是更好地实例化类如下:
MyExample<const int>
?这似乎过于严格,因为我永远无法返回shared_ptr<int>
?
It depends:
这取决于:
If you designed your module so that objects passed to it should not change again in the future, use
const T
as the underlying type.If you your module should be able to return non-const
T
pointers, you should useT
as your underlying type and probably have two different getters, one that returns mutable objects (std::shared_ptr<T>
) and another that returns non-mutable objects (std::shared_ptr<const T>
).
如果您将模块设计为传递给它的对象将来不应再次更改,请
const T
用作基础类型。如果您的模块应该能够返回非常量
T
指针,那么您应该将其T
用作基础类型,并且可能有两个不同的 getter,一个返回可变对象 (std::shared_ptr<T>
),另一个返回非可变对象 (std::shared_ptr<const T>
)。
And, even though I hope we just agreed you should notreturn std::shared_ptr<T>
if you have a const T
or std::shared_ptr<const T>
, you can:
而且,即使我希望我们刚刚同意,如果您有或,则不应返回,但您可以:std::shared_ptr<T>
const T
std::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);
Full blown example
完整的例子
Consider the following example that covers all the possible permutations of const
with std::shared_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;
}
Note: The following is true when managing all types of smart pointers. The assignment of pointers might differ (e.g. when handling unique_ptr
), but the concept it the same.
注意:以下适用于管理所有类型的智能指针。指针的分配可能不同(例如在处理时unique_ptr
),但概念是相同的。