C++ 我什么时候应该使用原始指针而不是智能指针?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6675651/
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
When should I use raw pointers over smart pointers?
提问by Alon Gubkin
After reading this answer, it looks like it is a best practice to use smart pointersas much as possible, and to reduce the usage of "normal"/raw pointers to minimum.
阅读此答案后,看起来尽可能多地使用智能指针并将“正常”/原始指针的使用量减少到最低限度是最佳做法。
Is that true?
真的吗?
回答by Armen Tsirunyan
No, it's not true. If a function needs a pointer and has nothing to do with ownership, then I strongly believe that a regular pointer should be passed for the following reasons:
不,这不是真的。如果一个函数需要一个指针并且与所有权无关,那么我坚信应该传递一个常规指针,原因如下:
- No ownership, therefore you don't know what kind of a smart pointer to pass
- If you pass a specific pointer, like
shared_ptr, then you won't be able to pass, say,scoped_ptr
- 没有所有权,因此您不知道要传递什么样的智能指针
- 如果您传递特定的指针,例如
shared_ptr,那么您将无法传递,例如,scoped_ptr
The rule would be this - if you know that an entity must take a certain kind of ownership of the object, alwaysuse smart pointers - the one that gives you the kind of ownership you need. If there is no notion of ownership, neveruse smart pointers.
规则是这样的——如果你知道一个实体必须拥有对象的某种所有权,那么总是使用智能指针——它为你提供了你需要的所有权。如果没有所有权的概念,永远不要使用智能指针。
Example1:
示例 1:
void PrintObject(shared_ptr<const Object> po) //bad
{
if(po)
po->Print();
else
log_error();
}
void PrintObject(const Object* po) //good
{
if(po)
po->Print();
else
log_error();
}
Example2:
示例2:
Object* createObject() //bad
{
return new Object;
}
some_smart_ptr<Object> createObject() //good
{
return some_smart_ptr<Object>(new Object);
}
回答by Luc Danton
Using smart pointers to manage ownership is the right thing to do. Conversely, using raw pointers wherever ownership is notan issue is notwrong.
使用智能指针来管理所有权是正确的做法。相反,在所有权不是问题的地方使用原始指针并没有错。
Here are some perfectly legitimate use of raw pointers (remember, it is always assumed they are non-owning):
以下是原始指针的一些完全合法的使用(请记住,始终假设它们是非拥有的):
where they compete with references
他们与参考文献竞争的地方
- argument passing; but references can't be null, so are preferable
- as class members to denote association rather than composition; usually preferable to references because the semantics of assignment are more straightforward and in addition an invariant set up by the constructors can ensure that they are not
0for the lifetime of the object - as a handle to a (possibly polymorphic) object owned somewhere else; references can't be null so again they are preferable
std::binduses a convention where arguments that are passed are copied into the resulting functor; howeverstd::bind(&T::some_member, this, ...)only makes a copy of the pointer whereasstd::bind(&T::some_member, *this, ...)copies the object;std::bind(&T::some_member, std::ref(*this), ...)is an alternative
- 参数传递;但引用不能为空,所以最好
- 作为班级成员来表示关联而不是组合;通常比引用更可取,因为赋值的语义更直接,此外构造函数设置的不变量可以确保它们不在
0对象的生命周期内 - 作为其他地方拥有的(可能是多态的)对象的句柄;引用不能为空,所以它们更可取
std::bind使用一种约定,其中传递的参数被复制到结果函子中;但是std::bind(&T::some_member, this, ...)只复制指针而std::bind(&T::some_member, *this, ...)复制对象;std::bind(&T::some_member, std::ref(*this), ...)是另一种选择
where they do notcompete with references
他们不与参考文献竞争的地方
- as iterators!
- argument passing of optionalparameters; here they compete with
boost::optional<T&> - as a handle to a (possibly polymorphic) object owned somewhere else, when they can't be declared at the site of initialization; again, competing with
boost::optional<T&>
- 作为迭代器!
- 可选参数的参数传递;他们在这里竞争
boost::optional<T&> - 作为在其他地方拥有的(可能是多态的)对象的句柄,当它们不能在初始化站点声明时;再次,与
boost::optional<T&>
As a reminder, it's almost always wrong to write a function (that is not a constructor, or a function member that e.g. takes ownership) that accepts a smart pointer unless it in turn pass it to a constructor (e.g. it's correct for std::asyncbecause semantically it's close to being a call to the std::threadconstructor). If it's synchronous, no need for the smart pointer.
提醒一下,编写一个接受智能指针的函数(不是构造函数,或者不是拥有所有权的函数成员)几乎总是错误的,除非它反过来将它传递给构造函数(例如,它是正确的,std::async因为在语义上它是接近调用std::thread构造函数)。如果是同步的,则不需要智能指针。
To recap, here's a snippet that demonstrates several of the above uses. We're writing and using a class that applies a functor to every element of an std::vector<int>while writing some output.
回顾一下,这是一个演示上述几种用法的片段。我们正在编写和使用一个类,该类std::vector<int>在编写一些输出时将函子应用于 an 的每个元素。
class apply_and_log {
public:
// C++03 exception: it's acceptable to pass by pointer to const
// to avoid apply_and_log(std::cout, std::vector<int>())
// notice that our pointer would be left dangling after call to constructor
// this still adds a requirement on the caller that v != 0 or that we throw on 0
apply_and_log(std::ostream& os, std::vector<int> const* v)
: log(&os)
, data(v)
{}
// C++0x alternative
// also usable for C++03 with requirement on v
apply_and_log(std::ostream& os, std::vector<int> const& v)
: log(&os)
, data(&v)
{}
// now apply_and_log(std::cout, std::vector<int> {}) is invalid in C++0x
// && is also acceptable instead of const&&
apply_and_log(std::ostream& os, std::vector<int> const&&) = delete;
// Notice that without effort copy (also move), assignment and destruction
// are correct.
// Class invariants: member pointers are never 0.
// Requirements on construction: the passed stream and vector must outlive *this
typedef std::function<void(std::vector<int> const&)> callback_type;
// optional callback
// alternative: boost::optional<callback_type&>
void
do_work(callback_type* callback)
{
// for convenience
auto& v = *data;
// using raw pointers as iterators
int* begin = &v[0];
int* end = begin + v.size();
// ...
if(callback) {
callback(v);
}
}
private:
// association: we use a pointer
// notice that the type is polymorphic and non-copyable,
// so composition is not a reasonable option
std::ostream* log;
// association: we use a pointer to const
// contrived example for the constructors
std::vector<int> const* data;
};
回答by Matthieu M.
The use of smart pointers is always recommended because they clearly document the ownership.
始终建议使用智能指针,因为它们清楚地记录了所有权。
What we really miss, however, is a "blank" smart pointer, one that does not imply any notion of ownership.
然而,我们真正错过的是一个“空白”的智能指针,它并不意味着任何所有权的概念。
template <typename T>
class ptr // thanks to Martinho for the name suggestion :)
{
public:
ptr(T* p): _p(p) {}
template <typename U> ptr(U* p): _p(p) {}
template <typename SP> ptr(SP const& sp): _p(sp.get()) {}
T& operator*() const { assert(_p); return *_p; }
T* operator->() const { assert(_p); return _p; }
private:
T* _p;
}; // class ptr<T>
This is, indeed, the simplest version of any smart pointer that may exist: a type that documents that it does not own the resource it points too.
这确实是可能存在的任何智能指针的最简单版本:一种记录它不拥有它所指向的资源的类型。
回答by Jeremy Friesner
One instance where reference counting (used by shared_ptr in particular) will break down is when you create a cycle out of the pointers (e.g. A points to B, B points to A, or A->B->C->A, or etc). In that case, none of the objects will ever be automatically freed, because they are all keeping each other's reference counts greater than zero.
引用计数(特别是由 shared_ptr 使用)会崩溃的一个实例是当您从指针创建一个循环时(例如,A 指向 B,B 指向 A,或 A->B->C->A,或等等)。在这种情况下,不会自动释放任何对象,因为它们都保持彼此的引用计数大于零。
For that reason, whenever I am creating objects that have a parent-child relationship (e.g. a tree of objects), I will use shared_ptrs in the parent objects to hold their child objects, but if the child objects need a pointer back to their parent, I will use a plain C/C++ pointer for that.
出于这个原因,每当我创建具有父子关系的对象(例如对象树)时,我都会在父对象中使用 shared_ptrs 来保存它们的子对象,但是如果子对象需要一个指向其父对象的指针,为此我将使用一个普通的 C/C++ 指针。
回答by iammilind
Few cases, where you may want to use pointers:
在少数情况下,您可能想要使用指针:
- Function pointers (obviously no smart pointer)
- Defining your own smart pointer or container
- Dealing with low level programming, where raw pointers are crucial
- Decaying from raw arrays
- 函数指针(显然没有智能指针)
- 定义您自己的智能指针或容器
- 处理低级编程,其中原始指针至关重要
- 从原始数组衰减
回答by Noah
I think a little bit more thorough answer was given here: Which kind of pointer do I use when?
我认为这里给出了更彻底的答案:我何时使用哪种指针?
Excerpted from that link: "Use dumb pointers (raw pointers) or references for non-owning referencesto resources and when you know that the resource will outlivethe referencing object / scope." (bold preserved from the original)
摘自该链接:“使用哑指针(原始指针)或对资源的非拥有引用的引用,并且当您知道资源将比引用对象/范围更持久时。” (粗体保留原始)
The problem is that if you're writing code for general use it's not always easy to be absolutely certain the object will outlive the raw pointer. Consider this example:
问题是,如果您正在编写用于一般用途的代码,则并不总是很容易绝对确定该对象将比原始指针更长寿。考虑这个例子:
struct employee_t {
employee_t(const std::string& first_name, const std::string& last_name) : m_first_name(first_name), m_last_name(last_name) {}
std::string m_first_name;
std::string m_last_name;
};
void replace_current_employees_with(const employee_t* p_new_employee, std::list<employee_t>& employee_list) {
employee_list.clear();
employee_list.push_back(*p_new_employee);
}
void main(int argc, char* argv[]) {
std::list<employee_t> current_employee_list;
current_employee_list.push_back(employee_t("John", "Smith"));
current_employee_list.push_back(employee_t("Julie", "Jones"));
employee_t* p_person_who_convinces_boss_to_rehire_him = &(current_employee_list.front());
replace_current_employees_with(p_person_who_convinces_boss_to_rehire_him, current_employee_list);
}
Much to its surprise, the replace_current_employees_with()function can inadvertently cause one of its parameters to be deallocated before it's finished using it.
出乎意料的是,该replace_current_employees_with()函数可能会在使用完毕之前无意中导致其参数之一被释放。
So even though it might at first seem like the replace_current_employees_with()function doesn't need ownership of it's parameters, it needs some kind of defense against the possiblity of its parameters being insidiously deallocated before it's finished using them. The simplest solution is to actually take (temporary shared) ownership of the parameter(s), presumably through a shared_ptr.
因此,尽管乍一看该replace_current_employees_with()函数似乎不需要其参数的所有权,但它需要某种防御措施,以防止其参数在完成使用之前被暗中解除分配的可能性。最简单的解决方案是实际获取(临时共享)参数的所有权,大概是通过shared_ptr.
But if you really don't want to take ownership, there is now a safe option - and this is the shameless plug portion of the answer - "registered pointers". "registered pointers" are smart pointers that behave like raw pointers, except that they are (automatically) set to null_ptrwhen the target object is destroyed, and by default, will throw an exception if you try to access an object that has already been deleted.
但是如果你真的不想拥有所有权,现在有一个安全的选择——这是答案的无耻插件部分——“注册指针”。“注册指针”是行为类似于原始指针的智能指针,除了它们null_ptr在目标对象被销毁时(自动)设置为,并且默认情况下,如果您尝试访问已删除的对象,则会抛出异常。
Also note that registered pointers can be "disabled" (automatically replaced with their raw pointer counterpart) with a compile-time directive, allowing them to be used (and incur overhead) in debug/test/beta modes only. So you should really have to resort actual raw pointers quite rarely.
另请注意,可以使用编译时指令“禁用”已注册的指针(自动替换为其原始指针对应项),仅允许在调试/测试/测试模式下使用它们(并产生开销)。所以你真的应该很少使用实际的原始指针。
回答by frank
I believe smart pointer should be used as much as possible, even in the situation where raw pointer is enough. unique_ptrcan help manage the resource life cycle while still staying small and fast. Don't look back!
我认为应该尽可能使用智能指针,即使在原始指针就足够的情况下。unique_ptr可以帮助管理资源生命周期,同时仍然保持小而快。不要回头!
回答by Trombe
It is true. I can not see the benefits of raw pointers over smart pointers, especially in a complex project.
是真的。我看不出原始指针相对于智能指针的优势,尤其是在复杂的项目中。
For tempory and lightweight usage, raw pointers are fine though.
对于临时和轻量级使用,原始指针虽然很好。

