Blue Bridge ROS Robot Modern C++ Study Notes Chapter 5 Smart Pointers and Memory Management

 

Why must learn modern C++, because it is very widely used in ROS, and you can't understand it without learning basic programs.

for example:

In fact, the description is not accurate. The pointers used in ROS1/2 include:

  1. std::shared_ptr
  2. std::unique_ptr
  3. std::weak_ptr 

E.g:

 virtual void 
 publish(std::unique_ptr<MessageT, MessageDeleter> & msg) 
 { 
   this->do_inter_process_publish(msg.get()); 
   if (store_intra_process_message_) { 
     // Take the pointer from the unique_msg, release it and pass as a void * 
     // to the ipm. The ipm should then capture it again as a unique_ptr of 
     // the correct type. 
     // TODO(wjwwood): 
     //   investigate how to transfer the custom deleter (if there is one) 
     //   from the incoming unique_ptr through to the ipm's unique_ptr. 
     //   See: http://stackoverflow.com/questions/11002641/dynamic-casting-for-unique-ptr 
     MessageT * msg_ptr = msg.get(); 
     msg.release(); 
     uint64_t message_seq = 
       store_intra_process_message_(intra_process_publisher_id_, msg_ptr, typeid(MessageT)); 
     rcl_interfaces::msg::IntraProcessMessage ipm; 
     ipm.publisher_id = intra_process_publisher_id_; 
     ipm.message_sequence = message_seq; 
     auto status = rcl_publish(&intra_process_publisher_handle_, &ipm); 
     if (RCL_RET_PUBLISHER_INVALID == status) { 
       rcl_reset_error();  // next call will reset error message if not context 
       if (rcl_publisher_is_valid_except_context(&intra_process_publisher_handle_)) { 
         rcl_context_t * context = rcl_publisher_get_context(&intra_process_publisher_handle_); 
         if (nullptr != context && !rcl_context_is_valid(context)) { 
           // publisher is invalid due to context being shutdown 
           return; 
         } 
       } 
     } 
     if (RCL_RET_OK != status) { 
       rclcpp::exceptions::throw_from_rcl_error(status, "failed to publish intra process message"); 
     } 
   } else { 
     // Always destroy the message, even if we don't consume it, for consistency. 
     msg.reset(); 
   } 
 } 

as well as:

#include <memory>

void unique_func_ref(std::unique_ptr<int> & msg) {}
void unique_func_value(std::unique_ptr<int> msg) {}

int main(int argc, char ** argv)
{
   std::unique_ptr<int> foo(new int);
   std::unique_ptr<int> bar(new int);

   unique_func_ref(foo);
   // does not compile
   // unique_func_value(bar);
   unique_func_value(std::move(bar));

   return 0;
}

 


Without learning modern C++, ROS1/2 robot programming can't even get into the door...

It has been 24 years since I wrote the first hello world in C++, but I am still a novice.

Just keep trying to learn.


Smart pointers have been introduced in C++11, so programmers no longer need to care about manually freeing memory.

(Overcome traditional C++, use newand deleteto "manual operation (can't forget memory leaks?)" to release resources.)


#include <iostream>
#include <memory>

void foo(std::shared_ptr<int> i)
{
    (*i)++;
}

int main()
{
    // auto pointer = new int(10); // illegal, no direct assignment
    // std::shared_ptr construction
    auto pointer = std::make_shared<int>(10);
    auto pointer2 = pointer;    // reference count + 1
    auto pointer3 = pointer;    // reference count + 1
    
    
    foo(pointer);
    std::cout << *pointer << std::endl; // 11
    int *p = pointer.get();             // does not increase reference count
    
    std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl;
    std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl;
    std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl;

    pointer2.reset();
    std::cout << "reset pointer2:" << std::endl;
    std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl;
    std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl;
    std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl;
    
    pointer3.reset();
    std::cout << "reset pointer3:" << std::endl;
    std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl;
    std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl;
    std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl;
    // std::cout << *pointer << std::endl; // reference count equals 0, illegal access


    // Before leaving the scope, the pointer is destructed and
    // the reference count is reduced to 0
    return 0;
}

 


#include <iostream>
#include <memory>

struct Foo {
    Foo()      { std::cout << "Foo::Foo" << std::endl;  }
    ~Foo()     { std::cout << "Foo::~Foo" << std::endl; }
    void foo() { std::cout << "Foo::foo" << std::endl;  }
};

void f(const Foo &) {
    std::cout << "f(const Foo&)" << std::endl;
}

int main() {
    std::unique_ptr<Foo> p1(std::make_unique<Foo>());
    
    // p1 is not empty, prints
    if (p1) p1->foo();
    {
        std::unique_ptr<Foo> p2(std::move(p1));
        
        // p2 is not empty, prints
        f(*p2);
        
        // p2 is not empty, prints
        if(p2) p2->foo();
        
        // p1 is empty, no prints
        if(p1) p1->foo();
        
        p1 = std::move(p2);
        
        // p2 is empty, no prints
        if(p2) p2->foo();
        std::cout << "p2 was destroyed" << std::endl;
    }
    // p1 is not empty, prints
    if (p1) p1->foo();
    
    // Foo instance will be destroyed when leaving the scope
}

 

 


#include <iostream>
#include <memory>

class A;
class B;

class A {
public:
    std::shared_ptr<B> pointer;
    ~A() {
        std::cout << "A was destroyed" << std::endl;
    }
};
class B {
public:
    std::shared_ptr<A> pointer;
    ~B() {
        std::cout << "B was destroyed" << std::endl;
    }
};
int main() {
    std::shared_ptr<A> a = std::make_shared<A>();
    std::shared_ptr<B> b = std::make_shared<B>();
    a->pointer = b;
    b->pointer = a;
    
    return 0;
}


 

 

 

Guess you like

Origin blog.csdn.net/ZhangRelay/article/details/124067964