zoukankan      html  css  js  c++  java
  • c++ 智能指针 传参

    智能指针就是作用域(scoping object)对象。即本地的值对象。出了作用域自动释放。用scoping object管理堆分配的对象,叫RAII(Resource Acquisition Is Initialization)。

    一个简单的RAII示例,自己实现个智能指针:

    template <typename RESTYPE>
    class ScopedResource final {
    public:
     ScopedResource() { managedResource = new RESTYPE(); }
     ~ScopedResource() { delete managedResource; }
     RESTYPE* operator->() const { return managedResource; }
    private:
     RESTYPE* managedResource;
    };

    智能指针使用:

    #include "ScopedResource.h"
    #include "ResourceType.h"
    void doSomething() {
     ScopedResource<ResourceType> resource;
     try {
     // ...do something with resource...
     resource->foo();
     } catch (...) {
     throw;
     }
    }

    可以看到,智能指针resource在栈上分配,而真实的对象是在堆上分配。所以虽然我们只是在栈上分配了resource智能对象,它确可以做为函数返回值返回,因为它的真实对象还是在堆里,返回的只是智能指针封装。而这个封装类ScopeResource并不大,赋值传递即可。更好的是,一旦这个resource智能对象出了作用域,析构函数~ScopedResource()会被触发,进而触发内部存放真正对象ResourceType类的析构,真正的对象就会被完美释放了。

    有三种,

    1. unique,
    2. shared(少用),
    3. weak:并不是所有的指针都有获得对象的所有权。我只使用这个对象。创建与销毁我不负责。以前使用方法:
    std::shared_ptr<ResourceType> resource = std::make_shared<ResourceType>();
    // ...
    ResourceType* rawPointerToResource = resource.get();

    我们获取后在使用,这时有可能resource被其他人释放。灾难!

    其实weak不是真正智能指针。这种类型指针和自动释放没有关系。它可以跟踪一个对象看是否还在。通过expired()判断,如果对象被销毁为true;如果还在,就可以通过lock()获取这个对象,升级成一个shared智能指针+1;出了作用域shared -1。

    weak示例:

    01 #include <memory>
    02
    03 void doSomething(const std::weak_ptr<ResourceType>& weakResource) {
    04 if (! weakResource.expired()) {
    05 // Now we know that weakResource contains a pointer to a valid object
    06 std::shared_ptr<ResourceType> sharedResource = weakResource.lock();
    07 // Use sharedResource...
    08 }
    09 }
    10
    11 int main() {
    12 auto sharedResource(std::make_shared<ResourceType>());
    13 std::weak_ptr<ResourceType> weakResource(sharedResource);
    14
    15 doSomething(weakResource);
    16 sharedResource.reset(); // Deletes the managed instance of ResourceType
    17 doSomething(weakResource);
    18
    19 return 0;
    20 }
    • 什么时候用什么指针:

    唯一拥有对象:用 

    不拥有:原始指针或者WeakPtr<>。WeakPtr<>是收取不到跟踪指针的销毁通知的。

    计数引用:scoped_refptr<>。慎用。在多线程时很容易搞不清谁拥有对象和销毁顺序。避免方式是每个线程访问一个,用postTask()到另外的线程。使用base::Bind()WeakPtr<>, 和其他工具可实现自动取消如果对象已经被销毁了。

    • 调用约定:

    1. 对于函数传参为std::unique_ptr<>。临时对象直接传(隐含move);其他对象显示调用move。
    // Foo() takes ownership of |bar|.
    void Foo(std::unique_ptr<Bar> bar);
    
    ...
    std::unique_ptr<Bar> bar_ptr(new Bar());
    Foo(std::move(bar_ptr));          // After this statement, |bar_ptr| is null.
    Foo(std::unique_ptr<Bar>(new Bar()));  // No need to use std::move() on temporaries.
    1. 返回值是std::unique_ptr<>,即调用者要拥有指针。由于有RAII,move()只在返回类型不一致才需要。
    class Base { ... };
    class Derived : public Base { ... };
    
    // Foo takes ownership of |base|, and the caller takes ownership of the returned
    // object.
    std::unique_ptr<Base> Foo(std::unique_ptr<Base> base) {
      if (cond) {
        return base;                           // Transfers ownership of |base| back to
                                               // the caller.
      }
    
      // Note that on these next codepaths, |base| is deleted on exit.
      if (cond2) {
        return std::unique_ptr<Base>(new Base()));  // No std::move() necessary on temporaries.
      }
      std::unique_ptr<Derived> derived(new Derived());
      return std::move(derived);               // Note that std::move() is necessary because
                                               // type of |derived| is different from the return
                                               // type of the function.
    }
    1. 不建议传智能指针引用。

    智能指针在函数间传递。如果共享对象时要更改,用shared_ptr.

    如果传引用,指针,需要确保它不会被外面reset,reassigned。对于全局shared,先copy一份成local的,对local在操作。

     

    对于函数返回值,如果调用者要操作,返回智能指针,只要值返回就行(因为1,move语义;2,返回值优化RVO,返回时不用move语句);。否则只是需要个handler,那就返回引用/指针。


    在chromium的回调中,base::OnceCallback 是个右值,调完run后即销毁了。

     

    void MyFunction1(base::OnceCallback<int(std::string, double)> my_callback) {
      // OnceCallback
      int result1 = std::move(my_callback).Run("my string 1", 1.0);
    
      // After running a OnceCallback, it's consumed and nulled out.
      DCHECK(!my_callback);
      ...
    }
    
    void MyFunction2(base::RepeatingCallback<int(std::string, double)> my_callback) {
      int result1 = my_callback.Run("my string 1", 1.0);
      // Run() can be called as many times as you wish for RepeatingCallback.
      int result2 = my_callback.Run("my string 2", 2);
      ...

    参考https://www.cnblogs.com/bigben0123/p/13857456.html

    参考:Clean C++ Sustainable Software Development Patterns and Best Practices with C++ 17


     

    base::TaskRunner允许异步,不管底层实现

    base::Closure是task的基类

    base::SequencedTaskRunnerHandle::Get()获取当前线程

    base::ThreadPool::PostTask()用例无锁运行在任何线程上。

     

    • PostTask()
    • PostDelayedTask() if you want to add a delay.
    • PostTaskAndReply() lets you post a task which will post a task back to your current thread when its done.
    • PostTaskAndReplyWithResult() to automatically pass the return value of the first call as argument to the second call.

     

    https://www.nextptr.com/question/qa1343387017/passing-stdunique_ptr-as-function-argument

    https://medium.com/pranayaggarwal25/passing-shared-ptr-and-unique-ptr-dc34a2f23010

    https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#f7-for-general-use-take-t-or-t-arguments-rather-than-smart-pointers

    https://eyakubovich.github.io/2018-08-05-smart-pointers-in-function-arguments/


    Smart Pointer Guidelines

    What are smart pointers?

    Smart pointers are a specific kind of "scoping object". They are like regular pointers but can automatically deallocate the object they point to when they go out of scope. Since C++ is not a garbage collected language, such functionality is important. The pattern where scoping objects are used to automatically manage the lifetime of heap-allocated objects is called RAII - Resource Acquisition IInitialization.

    Here's some sample use of std::unique_ptr<>, the most common type of smart pointer:

    // We can put a pointer into a std::unique_ptr<> at construction time...
    std::unique_ptr value(base::JSONReader::Read(data));
    std::unique_ptr foo_ptr(new Foo(...));
    
    // ...or by using reset().
    std::unique_ptr bar_ptr;      // Like "Bar* bar_ptr = nullptr;".
    bar_ptr.reset(new Bar(...));  // Now |bar_ptr| is non-nullptr and owns the object.
    
    // We can test the std::unique_ptr<> directly.
    if (!value)
      return false;
    
    // get() accesses the raw pointer underneath.
    Foo* raw_ptr = foo_ptr.get();
    
    // We can call through the std::unique_ptr<> as if it were a raw pointer.
    DictionaryValue* dict;
    if (!value->GetAsDictionary(&dict))
      return false;

    Why do we use them?

    Smart pointers ensure we properly destroy an object even if its creation and destruction are widely separated. They make functions simpler and safer by ensuring that no matter how many different exit paths exist, local objects are always cleaned up correctly. They help enforce that exactly one object owns another object at any given time, preventing both leaks and double-frees. Finally, their use clarifies ownership transference expectations at function calls.

    What types of smart pointers exist?

    The two common smart pointers in Chromium are std::unique_ptr<> and scoped_refptr<>. The former is used for singly-owned objects, while the latter is used for reference-counted objects (though normally you should avoid these -- see below). If you're familiar with C++11, scoped_refptr<> is similar in intent to std::shared_ptr<>.

    base/memory/ has a few other objects of interest:

    • WeakPtr<> is not actually a smart pointer; it functions like a pointer type, but rather than being used to automatically free objects, it's used to track whether an object owned elsewhere is still alive. When the object is destroyed, the WeakPtr<> will be automatically set to null, so you can see that it's no longer alive. (You still need to test for null before dereferencing -- a blind dereference of a null WeakPtr<> is the equivalent of dereferencing null, rather than a no-op.) This is somewhat like C++11's std::weak_ptr<>, but with a different API and fewer restrictions.

    When do we use each smart pointer?

    • Singly-owned objects - use std::unique_ptr<>. Specifically, these are for non-reference-counted, heap-allocated objects that you own.
    • Non-owned objects - use raw pointers or WeakPtr<>. Note that WeakPtr<>s must only be dereferenced on the same thread where they were created (usually by a WeakPtrFactory<>), and if you need to take some action immediately before or after an object is destroyed, you'd likely be better-served with some sort of callback or notification instead of a WeakPtr<>.
    • Ref-counted objects - use scoped_refptr<>, but better yet, rethink your design. Reference-counted objects make it difficult to understand ownership and destruction order, especially when multiple threads are involved. There is almost always another way to design your object hierarchy to avoid refcounting. Avoiding refcounting in multithreaded situations is usually easier if you restrict each class to operating on just one thread, and use PostTask() and the like to proxy calls to the correct thread. base::Bind()WeakPtr<>, and other tools make it possible to automatically cancel calls to such an object when it dies. Note that too much of our existing code uses refcounting, so just because you see existing code doing it does not mean it's the right solution. (Bonus points if you're able to clean up such cases.)
    • Platform-specific types - use one of the many platform-specific scoping objects, such as base::win::ScopedHandlebase::win::ScopedComPtr, or base::mac::ScopedCFTypeRef. Note that these may have slightly different usage patterns than std::unique_ptr<>; for example, they might be assigned as outparams via a .receive() type of method.

    What are the calling conventions involving different kinds of pointers?

    See the calling conventions section of the Chromium style guide for the rules; some common cases are illustrated below.

    • If a function takes a std::unique_ptr<>, that means it takes ownership of the argument. Callers need to use std::move() to indicate that they're passing ownership if the object being passed is not a temporary:
      // Foo() takes ownership of |bar|.
      void Foo(std::unique_ptr<Bar> bar);
      
      ...
      std::unique_ptr<Bar> bar_ptr(new Bar());
      Foo(std::move(bar_ptr));          // After this statement, |bar_ptr| is null.
      Foo(std::unique_ptr<Bar>(new Bar()));  // No need to use std::move() on temporaries.
    • If a function returns a std::unique_ptr<>, that means the caller takes ownership of the returned object. Usage of std::move() while returning an object is only needed if the return type of the function differs from the type of the local variable.
      class Base { ... };
      class Derived : public Base { ... };
      
      // Foo takes ownership of |base|, and the caller takes ownership of the returned
      // object.
      std::unique_ptr<Base> Foo(std::unique_ptr<Base> base) {
        if (cond) {
          return base;                           // Transfers ownership of |base| back to
                                                 // the caller.
        }
      
        // Note that on these next codepaths, |base| is deleted on exit.
        if (cond2) {
          return std::unique_ptr<Base>(new Base()));  // No std::move() necessary on temporaries.
        }
        std::unique_ptr<Derived> derived(new Derived());
        return std::move(derived);               // Note that std::move() is necessary because
                                                 // type of |derived| is different from the return
                                                 // type of the function.
      }
    • If a function takes or returns a raw pointer, it may mean no ownership is transferred, or it may not. Much of Chromium was written before std::unique_ptr<> existed, or by people unfamiliar with using it to indicate ownership transfers, and thus takes or returns raw pointers but transfers ownership in the process. Because the compiler can't enforce correct behavior here, this is less safe. Consider cleaning up such code, so that functions which take or return raw pointers never transfer ownership.

    What about passing or returning a smart pointer by reference?

    Don't do this.

    In principle, passing a const std::unique_ptr<T>& to a function which does not take ownership has some advantages over passing a T*: the caller can't accidentally pass in something utterly bogus (e.g. an int converted to a T*), and the caller is forced to guarantee the lifetime of the object persists across the function call. However, this declaration also forces callers to heap-allocate the objects in question, even if they could otherwise have declared them on the stack. Passing such arguments as raw pointers decouples the ownership issue from the allocation issue, so that the function is merely expressing a preference about the former. For the sake of simplicity and consistency, we avoid asking authors to balance these tradeoffs, and simply say to always use raw pointers.

    One exception is lambda functions used with STL algorithms operating on containers of smart pointers; these may have to take e.g. const std::unique_ptr<T>& in order to compile. And speaking of that...

    I want to use an STL container to hold pointers. Can I use smart pointers?

    Yes! As of C++11, you can store smart pointers in STL containers.

    General references on smart pointers

     

     


     

    https://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/

    GotW #91 Solution: Smart Pointer Parameters

    NOTE: Last year, I posted three new GotWs numbered #103-105. I decided leaving a gap in the numbers wasn’t best after all, so I am renumbering them to #89-91 to continue the sequence. Here is the updated version of what was GotW #105.

    How should you prefer to pass smart pointers, and why?

    Problem

    JG Question

    1. What are the performance implications of the following function declaration? Explain.

    void f( shared_ptr<widget> );
    

    Guru Questions

    2. What are the correctness implications of the function declaration in #1? Explain with clear examples.

    3. A colleague is writing a function f that takes an existing object of type widget as a required input-only parameter, and trying to decide among the following basic ways to take the parameter (omitting const):

    void f( widget* );              (a)
    void f( widget& );              (b)
    void f( unique_ptr<widget> );   (c)
    void f( unique_ptr<widget>& );  (d)
    void f( shared_ptr<widget> );   (e)
    void f( shared_ptr<widget>& );  (f)
    

    Under what circumstances is each appropriate? Explain your answer, including where const should or should not be added anywhere in the parameter type.

    (There are other ways to pass the parameter, but we will consider only the ones shown above.)

    Solution

    1. What are the performance implications of the following function declaration? Explain.

    void f( shared_ptr<widget> );
    

    A shared_ptr stores strong and weak reference counts (see GotW #89). When you pass by value, you have to copy the argument (usually) on entry to the function, and then destroy it (always) on function exit. Let’s dig into what this means.

    When you enter the function, the shared_ptr is copy-constructed, and this requires incrementing the strong reference count. (Yes, if the caller passes a temporary shared_ptr, you move-construct and so don’t have to update the count. But: (a) it’s quite rare to get a temporary shared_ptr in normal code, other than taking one function’s return value and immediately passing that to a second function; and (b) besides as we’ll see most of the expense is on the destruction of the parameter anyway.)

    When exiting the function, the shared_ptr is destroyed, and this requires decrementing its internal reference count.

    What’s so bad about a “shared reference count increment and decrement?” Two things, one related to the “shared reference count” and one related to the “increment and decrement.” It’s good to be aware of how this can incur performance costs for two reasons: one major and common, and one less likely in well-designed code and so probably more minor.

    First, the major reason is the performance cost of the “increment and decrement”: Because the reference count is an atomic shared variable (or equivalent), incrementing and decrementing it are internally-synchronized read-modify-write shared memory operations.

    Second, the less-likely minor reason is the potentially scalability-bustingly contentious nature of the “shared reference count”: Both increment and decrement update the reference count, which means that at the processor and memory level only one core at a time can be executing such an instruction on the same reference count because it needs exclusive access to the count’s cache line. The net result is that this causes some contention on the count’s cache line, which can affect scalability if it’s a popular cache line being touched by multiple threads in tight loops—such as if two threads are calling functions like this one in tight loops and accessing shared_ptrs that own the same object. “So don’t do that, thou heretic caller!” we might righteously say. Well and good, but the caller doesn’t always know when two shared_ptrs used on two different threads refer to the same object, so let’s not be quick to pile the wood around his stake just yet.

    As we will see, an essential best practice for any reference-counted smart pointer type is to avoid copying it unless you really mean to add a new reference. This cannot be stressed enough. This directly addresses both of these costs and pushes their performance impact down into the noise for most applications, and especially eliminates the second cost because it is an antipattern to add and remove references in tight loops.

    At this point, we will be tempted to solve the problem by passing the shared_ptr by reference. But is that really the right thing to do? It depends.

    2. What are the correctness implications of the function declaration in #1?

    The only correctness implication is that the function advertises in a clear type-enforced way that it will (or could) retain a copy of the shared_ptr.

    That this is the only correctness implication might surprise some people, because there would seem to be one other major correctness benefit to taking a copy of the argument, namely lifetime: Assuming the pointer is not already null, taking a copy of the shared_ptr guarantees that the function itself holds a strong refcount on the owned object, and that therefore the object will remain alive for the duration of the function body, or until the function itself chooses to modify its parameter.

    However, we already get this for free—thanks to structured lifetimes, the called function’s lifetime is a strict subset of the calling function’s call expression. Even if we passed the shared_ptr by reference, our function would as good as hold a strong refcount because the caller already has one—he passed us the shared_ptr in the first place, and won’t release it until we return. (Note this assumes the pointer is not aliased. You have to be careful if the smart pointer parameter could be aliased, but in this respect it’s no different than any other aliased object.)

    Guideline: Don’t pass a smart pointer as a function parameter unless you want to use or manipulate the smart pointer itself, such as to share or transfer ownership.

    Guideline: Prefer passing objects by value, *, or &, not by smart pointer.

    If you’re saying, “hey, aren’t raw pointers evil?”, that’s excellent, because we’ll address that next.

    3. A colleague is writing a function f that takes an existing object of type widget as a required input-only parameter, and trying to decide among the following basic ways to take the parameter (omitting const). Under what circumstances is each appropriate? Explain your answer, including where const should or should not be added anywhere in the parameter type.

    (a) and (b): Prefer passing parameters by * or &.

    void f( widget* );              (a)
    void f( widget& );              (b)
    

    These are the preferred way to pass normal object parameters, because they stay agnostic of whatever lifetime policy the caller happens to be using.

    Non-owning raw * pointers and & references are okay to observe an object whose lifetime we know exceeds that of the pointer or reference, which is usually true for function parameters. Thanks to structured lifetimes, by default arguments passed to f in the caller outlive f‘s function call lifetime, which is extremely useful (not to mention efficient) and makes non-owning * and & appropriate for parameters.

    Pass by * or & to accept a widget independently of how the caller is managing its lifetime. Most of the time, we don’t want to commit to a lifetime policy in the parameter type, such as requiring the object be held by a specific smart pointer, because this is usually needlessly restrictive. As usual, use a * if you need to express null (no widget), otherwise prefer to use a &; and if the object is input-only, write const widget* or const widget&.

    (c) Passing unique_ptr by value means “sink.”

    void f( unique_ptr<widget> );   (c)
    

    This is the preferred way to express a widget-consuming function, also known as a “sink.”

    Passing a unique_ptr by value is only possible by moving the object and its unique ownership from the caller to the callee. Any function like (c) takes ownership of the object away from the caller, and either destroys it or moves it onward to somewhere else.

    Note that, unlike some of the other options below, this use of a by-value unique_ptrparameter actually doesn’t limit the kind of object that can be passed to those managed by a unique_ptr. Why not? Because any pointer can be explicitly converted to a unique_ptr. If we didn’t use a unique_ptr here we would still have to express “sink” semantics, just in a more brittle way such as by accepting a raw owning pointer (anathema!) and documenting the semantics in comments. Using (c) is vastly superior because it documents the semantics in code, and requires the caller to explicitly move ownership.

    Consider the major alternative:

    // Smelly 20th-century alternative
    void bad_sink( widget* p );  // will destroy p; PLEASE READ THIS COMMENT
    
    // Sweet self-documenting self-enforcing modern version (c)
    void good_sink( unique_ptr<widget> p );
    

    And how much better (c) is:

    // Older calling code that calls the new good_sink is safer, because
    // it's clearer in the calling code that ownership transfer is going on
    // (this older code has an owning * which we shouldn't do in new code)
    //
    widget* pw = ... ; 
    
    bad_sink ( pw );             // compiles: remember not to use pw again!
    
    good_sink( pw );             // error: good
    good_sink( unique_ptr<widget>{pw} );  // need explicit conversion: good
    
    // Modern calling code that calls good_sink is safer, and cleaner too
    //
    unique_ptr<widget> pw = ... ;
    
    bad_sink ( pw.get() );       // compiles: icky! doesn't reset pw
    bad_sink ( pw.release() );   // compiles: must remember to use this way
    
    good_sink( pw );             // error: good!
    good_sink( move(pw) );       // compiles: crystal clear what's going on
    

    Guideline: Express a “sink” function using a by-value unique_ptr parameter.

    Because the callee will now own the object, usually there should be no const on the parameter because the const should be irrelevant.

    (d) Passing unique_ptr by reference is for in/out unique_ptr parameters.

    void f( unique_ptr<widget>& );  (d)
    

    This should only be used to accept an in/out unique_ptr, when the function is supposed to actually accept an existing unique_ptr and potentially modify it to refer to a different object. It is a bad way to just accept a widget, because it is restricted to a particular lifetime strategy in the caller.

    Guideline: Use a non-const unique_ptr& parameter only to modify the unique_ptr.

    Passing a const unique_ptr<widget>& is strange because it can accept only either null or a widget whose lifetime happens to be managed in the calling code via a unique_ptr, and the callee generally shouldn’t care about the caller’s lifetime management choice. Passing widget* covers a strict superset of these cases and can accept “null or a widget” regardless of the lifetime policy the caller happens to be using.

    Guideline: Don’t use a const unique_ptr& as a parameter; use widget* instead.

    I mention widget* because that doesn’t change the (nullable) semantics; if you’re being tempted to pass const shared_ptr<widget>&, what you really meant was widget* which expresses the same information. If you additionally know it can’t be null, though, of course use widget&.

    (e) Passing shared_ptr by value implies taking shared ownership.

    void f( shared_ptr<widget> );   (e)
    

    As we saw in #2, this is recommended only when the function wants to retain a copy of the shared_ptr and share ownership. In that case, a copy is needed anyway so the copying cost is fine. If the local scope is not the final destination, just std::move the shared_ptr onward to wherever it needs to go.

    Guideline: Express that a function will store and share ownership of a heap object using a by-value shared_ptr parameter.

    Otherwise, prefer passing a * or & (possibly to const) instead, since that doesn’t restrict the function to only objects that happen to be owned by shared_ptrs.

    (f) Passing shared_ptr& is useful for in/out shared_ptr manipulation.

    void f( shared_ptr<widget>& );  (f)
    

    Similarly to (d), this should mainly be used to accept an in/out shared_ptr, when the function is supposed to actually modify the shared_ptr itself. It’s usually a bad way to accept a widget, because it is restricted to a particular lifetime strategy in the caller.

    Note that per (e) we pass a shared_ptr by value if the function will share ownership. In the special case where the function might share ownership, but doesn’t necessarily take a copy of its parameter on a given call, then pass a const shared_ptr& to avoid the copy on the calls that don’t need it, and take a copy of the parameter if and when needed.

    Guideline: Use a non-const shared_ptr& parameter only to modify the shared_ptr. Use a const shared_ptr& as a parameter only if you’re not sure whether or not you’ll take a copy and share ownership; otherwise use widget*instead (or if not nullable, a widget&).

    Acknowledgments

    Thanks in particular to the following for their feedback to improve this article: mttpd, zahirtezcan, Jon, GregM, Andrei Alexandrescu.

  • 相关阅读:
    刘翔那点事
    网站建站模板
    搞笑!from 饮水思源
    我de虚拟经济学系列第一章 经济危机拼命建桥
    IT民工系列——c#操作Microsoft IE,实现自动登录吧!
    商业智能的发展及其应用
    我de虚拟经济学系列第三章 常见的致富之路
    IT民工系列——c#操作EditGrid,自己做一个在线Excel数据库吧!
    Asp.net下的Singleton模式
    asp.net 控件功能小结
  • 原文地址:https://www.cnblogs.com/bigben0123/p/13847995.html
Copyright © 2011-2022 走看看