为什么要使用指针而不是对象本身?

本文翻译自:Why should I use a pointer rather than the object itself?

I'm coming from a Java background and have started working with objects in C++. 我来自Java背景,已经开始使用C ++处理对象。 But one thing that occurred to me is that people often use pointers to objects rather than the objects themselves, for example this declaration: 但是我想到的一件事是,人们经常使用指向对象的指针,而不是对象本身,例如以下声明:

Object *myObject = new Object;

rather than: 而不是:

Object myObject;

Or instead of using a function, let's say testFunc() , like this: 或者不使用函数,而是说testFunc() ,如下所示:

myObject.testFunc();

we have to write: 我们必须写:

myObject->testFunc();

But I can't figure out why should we do it this way. 但是我不知道为什么我们要这样做。 I would assume it has to do with efficiency and speed since we get direct access to the memory address. 我认为这与效率和速度有关,因为我们可以直接访问内存地址。 Am I right? 我对吗?


#1楼

参考:https://stackoom.com/question/1UvD4/为什么要使用指针而不是对象本身


#2楼

In C++, objects allocated on the stack (using Object object; statement within a block) will only live within the scope they are declared in. When the block of code finishes execution, the object declared are destroyed. 在C ++中,在堆栈上分配的Object object; (使用Object object;块中的语句)将仅在声明它们的范围内存在。当代码块完成执行时,声明的对象将被销毁。 Whereas if you allocate memory on heap, using Object* obj = new Object() , they continue to live in heap until you call delete obj . 而如果您使用Object* obj = new Object()在堆上分配内存,则它们将继续存在于堆中,直到您调用delete obj为止。

I would create an object on heap when I like to use the object not only in the block of code which declared/allocated it. 当我不仅要在声明/分配它的代码块中使用该对象时,还会在堆上创建一个对象。


#3楼

It's very unfortunate that you see dynamic allocation so often. 不幸的是,您经常看到动态分配。 That just shows how many bad C++ programmers there are. 这只表明有多少坏的C ++程序员。

In a sense, you have two questions bundled up into one. 从某种意义上说,您将两个问题捆绑在一起。 The first is when should we use dynamic allocation (using new )? 首先是何时应该使用动态分配(使用new )? The second is when should we use pointers? 第二个是什么时候应该使用指针?

The important take-home message is that you should always use the appropriate tool for the job . 重要提示信息是您应始终使用适当的工具进行作业 In almost all situations, there is something more appropriate and safer than performing manual dynamic allocation and/or using raw pointers. 在几乎所有情况下,都比执行手动动态分配和/或使用原始指针更合适,更安全。

Dynamic allocation 动态分配

In your question, you've demonstrated two ways of creating an object. 在您的问题中,您演示了两种创建对象的方法。 The main difference is the storage duration of the object. 主要区别是对象的存储时间。 When doing Object myObject; 当做Object myObject; within a block, the object is created with automatic storage duration, which means it will be destroyed automatically when it goes out of scope. 在一个块内,将创建具有自动存储持续时间的对象,这意味着它将在超出范围时自动销毁。 When you do new Object() , the object has dynamic storage duration, which means it stays alive until you explicitly delete it. 当您执行new Object() ,对象具有动态存储持续时间,这意味着该对象将保持活动状态,直到您明确delete它为止。 You should only use dynamic storage duration when you need it. 您仅应在需要时使用动态存储期限。 That is, you should always prefer creating objects with automatic storage duration when you can . 也就是说, 您应该始终喜欢在可以的时候创建具有自动存储持续时间的对象

The main two situations in which you might require dynamic allocation: 您可能需要动态分配的两种主要情况:

  1. You need the object to outlive the current scope - that specific object at that specific memory location, not a copy of it. 您需要使对象的寿命超出当前作用域 -该特定对象位于该特定内存位置,而不是其副本。 If you're okay with copying/moving the object (most of the time you should be), you should prefer an automatic object. 如果您可以复制/移动对象(大多数情况下应该如此),则应该使用自动对象。
  2. You need to allocate a lot of memory , which may easily fill up the stack. 您需要分配大量内存 ,这很容易填满堆栈。 It would be nice if we didn't have to concern ourselves with this (most of the time you shouldn't have to), as it's really outside the purview of C++, but unfortunately, we have to deal with the reality of the systems we're developing for. 如果我们不必为此担心(大多数时候您不必担心),那将是很好的,因为它确实超出了C ++的范围,但是不幸的是,我们必须处理系统的实际情况。我们正在开发。

When you do absolutely require dynamic allocation, you should encapsulate it in a smart pointer or some other type that performs RAII (like the standard containers). 当您确实需要动态分配时,应将其封装在智能指针或其他执行RAII的类型中(例如标准容器)。 Smart pointers provide ownership semantics of dynamically allocated objects. 智能指针提供了动态分配对象的所有权语义。 Take a look at std::unique_ptr and std::shared_ptr , for example. 例如,看一下std::unique_ptrstd::shared_ptr If you use them appropriately, you can almost entirely avoid performing your own memory management (see the Rule of Zero ). 如果正确使用它们,则几乎可以完全避免执行自己的内存管理(请参见零规则 )。

Pointers 指针

However, there are other more general uses for raw pointers beyond dynamic allocation, but most have alternatives that you should prefer. 但是,除动态分配外,原始指针还有其他更通用的用法,但大多数都有您应首选的替代方法。 As before, always prefer the alternatives unless you really need pointers . 和以前一样, 除非您确实需要使用指针,否则请始终选择替代方法

  1. You need reference semantics . 您需要参考语义 Sometimes you want to pass an object using a pointer (regardless of how it was allocated) because you want the function to which you're passing it to have access that that specific object (not a copy of it). 有时您想要使用指针传递对象(无论如何分配),因为您希望传递给它的函数可以访问该特定对象(而不是其副本)。 However, in most situations, you should prefer reference types to pointers, because this is specifically what they're designed for. 但是,在大多数情况下,您应该更喜欢引用类型而不是指针,因为这是专门为它们设计的。 Note this is not necessarily about extending the lifetime of the object beyond the current scope, as in situation 1 above. 请注意,这不一定是要延长对象的生存期,使其超出当前范围,如上述情况1所示。 As before, if you're okay with passing a copy of the object, you don't need reference semantics. 和以前一样,如果您可以传递对象的副本,则不需要引用语义。

  2. You need polymorphism . 您需要多态性 You can only call functions polymorphically (that is, according to the dynamic type of an object) through a pointer or reference to the object. 您只能通过指针或对对象的引用来多态调用函数(即,根据对象的动态类型)。 If that's the behavior you need, then you need to use pointers or references. 如果这是您需要的行为,则需要使用指针或引用。 Again, references should be preferred. 同样,引用应该是首选。

  3. You want to represent that an object is optional by allowing a nullptr to be passed when the object is being omitted. 您想通过允许在省略对象时传递nullptr 来表示该对象是可选的。 If it's an argument, you should prefer to use default arguments or function overloads. 如果是参数,则应该首选使用默认参数或函数重载。 Otherwise, you should preferably use a type that encapsulates this behavior, such as std::optional (introduced in C++17 - with earlier C++ standards, use boost::optional ). 否则,您最好使用一种封装此行为的类型,例如std::optional (在C ++ 17中引入-对于较早的C ++标准,请使用boost::optional )。

  4. You want to decouple compilation units to improve compilation time . 您希望解耦编译单元以缩短编译时间 The useful property of a pointer is that you only require a forward declaration of the pointed-to type (to actually use the object, you'll need a definition). 指针的有用属性是,您只需要指向类型的前向声明(要实际使用该对象,您需要一个定义)。 This allows you to decouple parts of your compilation process, which may significantly improve compilation time. 这使您可以使编译过程的各个部分脱钩,从而可以大大缩短编译时间。 See the Pimpl idiom . 请参阅“ Pimpl习惯用法”

  5. You need to interface with a C library or a C-style library. 您需要与C库或C样式库接口。 At this point, you're forced to use raw pointers. 在这一点上,您不得不使用原始指针。 The best thing you can do is make sure you only let your raw pointers loose at the last possible moment. 您可以做的最好的事情是确保仅在可能的最后时刻松开原始指针。 You can get a raw pointer from a smart pointer, for example, by using its get member function. 您可以从智能指针中获取原始指针,例如,通过使用其get成员函数。 If a library performs some allocation for you which it expects you to deallocate via a handle, you can often wrap the handle up in a smart pointer with a custom deleter that will deallocate the object appropriately. 如果库为您执行了一些分配,希望您可以通过句柄对其进行分配,则通常可以使用自定义删除程序将句柄包装在智能指针中,该删除器将适当地分配对象。


#4楼

Let's say that you have class A that contain class B When you want to call some function of class B outside class A you will simply obtain a pointer to this class and you can do whatever you want and it will also change context of class B in your class A 比方说,你有class A包含class B当你想调用的一些函数class B以外的class A ,你只会获得一个指向这个类,你可以做任何你想要的,它也将发生变化的情况下class B中你的class A

But be careful with dynamic object 但是要注意动态物体


#5楼

Technically it is a memory allocation issue, however here are two more practical aspects of this. 从技术上讲,这是一个内存分配问题,但是这里有两个更实际的方面。 It has to do with two things: 1) Scope, when you define an object without a pointer you will no longer be able to access it after the code block it is defined in, whereas if you define a pointer with "new" then you can access it from anywhere you have a pointer to this memory until you call "delete" on the same pointer. 它与两件事有关:1)范围,当您定义没有指针的对象时,在定义了该对象的代码块之后,您将不再能够访问它,而如果您使用“ new”定义了指针,则您可以从您有指向该内存的指针的任何位置访问它,直到您在同一指针上调用“删除”为止。 2) If you want to pass arguments to a function you want to pass a pointer or a reference in order to be more efficient. 2)如果要将参数传递给函数,则需要传递指针或引用以提高效率。 When you pass an Object then the object is copied, if this is an object that uses a lot of memory this might be CPU consuming (eg you copy a vector full of data). 传递对象时,将复制该对象,如果这是一个使用大量内存的对象,则可能会占用CPU(例如,复制充满数据的向量)。 When you pass a pointer all you pass is one int (depending of implementation but most of them are one int). 当您传递一个指针时,您传递的只是一个整数(取决于实现,但大多数都是一个整数)。

Other than that you need to understand that "new" allocates memory on the heap that needs to be freed at some point. 除此之外,您还需要了解“ new”会在需要在某个时刻释放的堆上分配内存。 When you don't have to use "new" I suggest you use a regular object definition "on the stack". 当您不必使用“新”时,建议您在“堆栈上”使用常规的对象定义。


#6楼

There are many use cases for pointers. 指针有很多用例。

Polymorphic behavior . 多态行为 For polymorphic types, pointers (or references) are used to avoid slicing: 对于多态类型,使用指针(或引用)来避免切片:

class Base { ... };
class Derived : public Base { ... };

void fun(Base b) { ... }
void gun(Base* b) { ... }
void hun(Base& b) { ... }

Derived d;
fun(d);    // oops, all Derived parts silently "sliced" off
gun(&d);   // OK, a Derived object IS-A Base object
hun(d);    // also OK, reference also doesn't slice

Reference semantics and avoiding copying . 参考语义和避免复制 For non-polymorphic types, a pointer (or a reference) will avoid copying a potentially expensive object 对于非多态类型,指针(或引用)将避免复制可能昂贵的对象

Base b;
fun(b);  // copies b, potentially expensive 
gun(&b); // takes a pointer to b, no copying
hun(b);  // regular syntax, behaves as a pointer

Note that C++11 has move semantics that can avoid many copies of expensive objects into function argument and as return values. 请注意,C ++ 11具有移动语义,可以避免将许多昂贵的对象复制到函数参数中并作为返回值。 But using a pointer will definitely avoid those and will allow multiple pointers on the same object (whereas an object can only be moved from once). 但是使用指针肯定会避免这些情况,并且将允许在同一对象上使用多个指针(而一个对象只能移动一次)。

Resource acquisition . 资源获取 Creating a pointer to a resource using the new operator is an anti-pattern in modern C++. 在现代C ++中,使用new运算符创建指向资源的指针是一种反模式 Use a special resource class (one of the Standard containers) or a smart pointer ( std::unique_ptr<> or std::shared_ptr<> ). 使用特殊的资源类(标准容器之一)或智能指针std::unique_ptr<>std::shared_ptr<> )。 Consider: 考虑:

{
    auto b = new Base;
    ...       // oops, if an exception is thrown, destructor not called!
    delete b;
}

vs.

{
    auto b = std::make_unique<Base>();
    ...       // OK, now exception safe
}

A raw pointer should only be used as a "view" and not in any way involved in ownership, be it through direct creation or implicitly through return values. 原始指针应仅用作“视图”,而不能以任何方式涉及所有权,无论是通过直接创建还是通过返回值隐式使用。 See also this Q&A from the C++ FAQ . 另请参阅C ++常见问题解答中的此问答

More fine-grained life-time control Every time a shared pointer is being copied (eg as a function argument) the resource it points to is being kept alive. 更精细的生命周期控制每次复制共享指针(例如,作为函数参数)时,它所指向的资源都会保持活动状态。 Regular objects (not created by new , either directly by you or inside a resource class) are destroyed when going out of scope. 超出范围时,常规对象(不是由new创建的,不是由您直接创建的,或者由您直接在资源类内部创建的)被破坏的。

发布了0 篇原创文章 · 获赞 72 · 访问量 54万+

猜你喜欢

转载自blog.csdn.net/w36680130/article/details/105216737