自动类型推导
在上一节的2_ws/src/demo_cpp_pkg/src下面新建learn_auto.cpp文件
#include "iostream"int main()
{auto x =5; // int x=5;auto y = 3.14f; //float y=3.14auto z ='a';std::cout<<x<<std::endl;std::cout<<y<<std::endl;std::cout<<z<<std::endl;return 0;
}
auto
关键字用于自动推导变量的类型,auto不是自动,简化代码编写,可以再给变量赋值时根据等号右侧的类型自动推导变量的类型。
注意:使用auto定义变量时必须对其进行初始化.
智能指针
新建learn_shared_ptr.cpp文件
#include <iostream>
#include <memory>int main()
{auto p1 = std::make_shared<std::string>("this is a str.");// std::make_shared<数据类型/类>(参数)std::cout<< "p1 的引用计数:"<< p1.use_count() << ",指向内存地址:"<<p1.get() << std::endl;//1auto p2 =p1;std::cout<< "p1 的引用计数:"<< p1.use_count() << ",指向内存地址:"<<p1.get() << std::endl;//2std::cout<< "p2 的引用计数:"<< p2.use_count() << ",指向内存地址:"<<p2.get() << std::endl;//2p1.reset();//释放引用,不指向“this is a str” 所在内存std::cout<< "p1 的引用计数:"<< p1.use_count() << ",指向内存地址:"<<p1.get() << std::endl;//0std::cout<< "p2 的引用计数:"<< p2.use_count() << ",指向内存地址:"<<p2.get() << std::endl;//2-1=1std::cout<< "p2 的指向内存地址数据:"<< p2->c_str() << std::endl;//2-1=1return 0;
}
其中,std::make_shared 创建的类型就是std::shared_ptr<std::string> p1。
老师说是高效的内存分配,分配包括对象和控制块(控制块包含了引用计数等等信息)的连续内存。指针通常用于存储其他变量地址,当指针指向的动态内存不再使用时,需要手动调用free进行释放。忘记或者提前释放会造成问题,智能指针的计数器就是解决这个问题,帮助我们进行动态分配对象(new出来的对象)的生命周期的管理,能够有效防止内存泄漏。
过程:拷贝一个 shared_ptr,计数器都会递增,当我们给 shared_ptr 赋予一个新值或是 shared_ptr 被销毁,计数器递减,当动态对象不再被使用时,shared_ptr 类会自动释放动态对象。
运行结果:
bohu@bohu-TM1701:~/2/2_ws$ ros2 run demo_cpp_pkg learn_shared_ptr
p1 的引用计数:1,指向内存地址:0x5cf166490ec0
p1 的引用计数:2,指向内存地址:0x5cf166490ec0
p2 的引用计数:2,指向内存地址:0x5cf166490ec0
p1 的引用计数:0,指向内存地址:0
p2 的引用计数:1,指向内存地址:0x5cf166490ec0
p2 的指向内存地址数据:this is a str.
虽然重置了p1,但是P2 仍然持有资源,所以资源并不会释放。
Lambda表达式
Lambda表达式是C++11引入的一种匿名函数,lambda表达式的基本语法如下:
[capture list] (parameter list) -> return type { function body }
capture list
是捕获列表,用于指定 Lambda表达式可以访问的外部变量,以及是按值还是按引用的方式访问。
parameter list 是参数列表,用于表示 Lambda表达式的参数,可以为空,表示没有参数,也可以和普通函数一样指定参数的类型和名称,还可以在 c++14 中使用 auto 关键字来实现泛型参数。
return type 是返回值类型,用于指定 Lambda表达式的返回值类型,可以省略,表示由编译器根据函数体推导,也可以使用 -> 符号显式指定,还可以在 c++14 中使用 auto 关键字来实现泛型返回值。
function body 是函数体,用于表示 Lambda表达式的具体逻辑,可以是一条语句,也可以是多条语句,还可以在 c++14 中使用 constexpr 来实现编译期计算。
新建learn_lambda1.cpp
int main()
{// 定义一个 Lambda表达式,计算两数之和auto add = [] (int a,int b) -> int {return a+b;};int sum = add(3,5);auto print_sum = [sum]()->void{ std::cout<< "3+5="<<sum<< std::endl;};print_sum();int c = 100;int d = 200;// 定义一个 Lambda表达式,按值捕获外部 c 和 dauto add2 = [c, d] () -> int { return c + d; };// 调用 Lambda表达式std::cout << c<<"+"<<d<<"="<<add2() << std::endl; // 输出 300return 0;
}
修改CMakeLists.txt,编译之后运行
bohu@bohu-TM1701:~/2/2_ws$ ros2 run demo_cpp_pkg learn_lambda1
3+5=8
100+200=300
运行结果符合预期。现在可能还体会不到lambda带来的好处,后面再慢慢体会。
函数包装器
function函数包装器也叫适配器,C++中方法实现函数有很多类型,举例的就有3种。
函数包装器的格式
function<函数返回值类型(函数参数类型)>包装器名称=要包装的函数。
新建learn_function.cpp
#include <iostream>
#include <functional>
using namespace std;//自由函数
void save_with_free_fun(const string &file_name)
{cout << "调用了自由函数,保存:"<< file_name<< endl;
}//成员函数
class FileSave
{
private:/* data */
public:FileSave(/* args */)=default;~ FileSave()= default;void save_with_member_fun(const string &file_name){cout << "调用了成员函数,保存:"<< file_name<< endl;};
};int main()
{FileSave file_save;auto save_with_lambda_fun =[](const string &file_name)->void {cout << "调用了lambda函数,保存:"<< file_name<< endl;};// save_with_free_fun("file.txt");// file_save.save_with_member_fun("file.txt");function<void(const string&)> save1 = save_with_free_fun;function<void(const string&)> save2 = save_with_lambda_fun;//成员函数,放入包装器function<void(const string&)> save3 = bind(&FileSave::save_with_member_fun,&file_save,placeholders::_1);save1("file.txt");save2("file.txt");save3("file.txt");return 0;
}
有3种函数类型:自由函数、lambda函数、成员函数。前2种容易理解,成员函数麻烦点,其中std::bind可以看作一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来适应原对象的参数列表。bind的格式如下:
auto newCallable = bind(callable, arg_list);
作用当调用newCallable
时,会调用callable
,并传给它arg_list
中的参数。里面的std::placeholders(占位符)表示新的可调用对象的第几个参数,而且与原函数的该占位符所在位置的进行匹配;
修改CMakeLists.txt,编译之后运行
bohu@bohu-TM1701:~/2/2_ws$ ros2 run demo_cpp_pkg learn_function
调用了自由函数,保存:file.txt
调用了lambda函数,保存:file.txt
调用了成员函数,保存:file.txt
ros2 后面把成员函数当回调函数使用,出于安全保护,不暴漏对象暴露封装的函数。