计时:
// 方法1
#include <chrono>
std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now();
// do something
std::chrono::steady_clock::time_point t2 = std::chrono::steady_clock::now();
std::chrono::duration<double> time_used = std::chrono::duration_cast<std::chrono::duration<double>>(t2-t1);
std::cout << "use time: " << time_used.count() << std::endl;
// 方法2
#include <time.h>
std::clock_t t1 = std::clock();
// do something
std::clock_t t2 = std::clock();
double time_used = (double)(t2 - t1) / CLOCKS_PER_SEC;
std::cout << "use time: " << time_used << std::endl;
#include <ros/time.h>
#include <chrono>
using Time = std::chrono::time_point<std::chrono::system_clock, Duration>;
Time t = Time(std::chrono::system_clock::now());
int64_t timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(t.time_since_epoch()).count();
// A-LOAM
#include <ctime>
#include <cstdlib>
#include <chrono>
class TicToc
{
public:
TicToc() {
tic();
}
void tic() {
start = std::chrono::system_clock::now();
}
double toc() {
end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end - start;
return elapsed_seconds.count() * 1000;
}
private:
std::chrono::time_point<std::chrono::system_clock> start, end;
};
睡眠延时:
#include <thread>
std::this_thread::sleep_for(std::chrono::milliseconds(3000)); // 3秒
std::this_thread::sleep_for(std::chrono::seconds(1)); // 1秒
// 在linux下
#include <unistd.h>
sleep(5) // 5秒
ros::Duration(0.5).sleep(); // 0.5秒
随机数生成:
DUtils::Random::RandomInt(0,n);//随机生成0~n间的整数
std::random_device rd;
int x = rd() % 100;
#include <time.h>
srand(time(0)); // 设置种子
int x = std::rand() % 100;
float b = 255 * float(std::rand()) / RAND_MAX;
RNG rng;
//产生64位整数
int N1 = rng;
//产生[0,1)范围内均匀分布的double类型数据。
double N1d = rng.uniform(0.,1.);
//产生符合均值为1,标准差为0.5的高斯分布的随机数
double N1g = 1 + rng.gaussian(0.5);
高斯噪声生成:
cv::RNG rng;
double w_sigma=1.0;
rng.gaussian(w_sigma)
from ROS gazebo plugin (利用的The Box-Muller transformation算法)
下面的函数c函数rand_r在生成随机数之后,种子seed的值会自动变化
unsigned int seed = 0;
double gazebo::GazeboRosImuSensor::GuassianKernel(double mu, double sigma)
{
// generation of two normalized uniform random variables
double U1 = static_cast<double>(rand_r(&seed)) / static_cast<double>(RAND_MAX);
double U2 = static_cast<double>(rand_r(&seed)) / static_cast<double>(RAND_MAX);
// using Box-Muller transform to obtain a varaible with a standard normal distribution
double Z0 = sqrt(-2.0 * ::log(U1)) * cos(2.0*M_PI * U2);
// scaling
Z0 = sigma * Z0 + mu;
return Z0;
}
other: std::rand - cppreference.com
最大最小值、无穷判断
#include <cmath>
// std::isnormal() 判断给定的数字是否正常(不是零、无穷、NAN)。如果数字正常,则此函数返回1,否则返回零。
std::cout << std::boolalpha << std::isnormal(NAN) << std::endl; // false
std::cout << std::boolalpha << std::isnormal(INFINITY) << std::endl; // false
std::cout << std::boolalpha << std::isnormal(0.0) << std::endl; // false
std::cout << std::boolalpha << std::isnormal(0.0 / 0.0) << std::endl; // false
std::cout << std::boolalpha << std::isnormal(INFINITY - INFINITY) << std::endl; // false
// std::isnan() 判断是否非数(NaN)值
std::cout << std::boolalpha << std::isnan(NAN) << std::endl; // true
std::cout << std::boolalpha << std::isnan(INFINITY) << std::endl; // false
std::cout << std::boolalpha << std::isnan(0.0) << std::endl; // false
std::cout << std::boolalpha << std::isnan(0.0 / 0.0) << std::endl; // true
std::cout << std::boolalpha << std::isnan(INFINITY - INFINITY) << std::endl; // true
// std::isfinite() 判断是否为有限值
std::cout << std::boolalpha << std::isfinite(NAN) << std::endl; // false
std::cout << std::boolalpha << std::isfinite(INFINITY) << std::endl; // false
std::cout << std::boolalpha << std::isfinite(0.0) << std::endl; // true
std::cout << std::boolalpha << std::isfinite(0.0 / 0.0) << std::endl; // false
std::cout << std::boolalpha << std::isfinite(INFINITY - INFINITY) << std::endl; // false
// std::isinf() 判断是否无穷
std::cout << std::boolalpha << std::isinf(NAN) << std::endl; // false
std::cout << std::boolalpha << std::isinf(INFINITY) << std::endl; // true
std::cout << std::boolalpha << std::isinf(0.0) << std::endl; // false
std::cout << std::boolalpha << std::isinf(0.0 / 0.0) << std::endl; // false
std::cout << std::boolalpha << std::isinf(INFINITY - INFINITY) << std::endl; // false
#include <climits> // c++
// #include <limits.h> // c
std::cout << INT_MIN << std::endl; // -2147483648
std::cout << INT_MAX << std::endl; // 2147483647
std::cout << FLT_MIN << std::endl; // 1.17549e-38
std::cout << FLT_MAX << std::endl; // 3.40282e+38
std::cout << DBL_MIN << std::endl; // 2.22507e-308
std::cout << DBL_MAX << std::endl; // 1.79769e+308
#include <limits>
std::cout << std::numeric_limits<int>::lowest() << std::endl; // -2147483648
std::cout << std::numeric_limits<int>::min() << std::endl; // -2147483648
std::cout << std::numeric_limits<int>::max() << std::endl; // 2147483647
std::cout << std::numeric_limits<float>::lowest() << std::endl; // -3.40282e+38
std::cout << std::numeric_limits<float>::min() << std::endl; // 1.17549e-38
std::cout << std::numeric_limits<float>::max() << std::endl; // 3.40282e+38
std::cout << std::numeric_limits<double>::lowest() << std::endl; // -1.79769e+308
std::cout << std::numeric_limits<double>::min() << std::endl; // 2.22507e-308
std::cout << std::numeric_limits<double>::max() << std::endl; // 1.79769e+308
std::cout << std::numeric_limits<int>::epsilon() << std::endl; // 0
std::cout << std::numeric_limits<int>::infinity() << std::endl; // 0
std::cout << std::numeric_limits<int>::digits << std::endl; // 31
std::cout << std::numeric_limits<int>::is_signed << std::endl; // 1
std::cout << std::numeric_limits<int>::has_infinity << std::endl; // 0
std::cout << std::numeric_limits<float>::epsilon() << std::endl; // 1.19209e-07
std::cout << std::numeric_limits<float>::infinity() << std::endl; // inf
std::cout << std::numeric_limits<float>::digits << std::endl; // 24
std::cout << std::numeric_limits<float>::is_signed << std::endl; // 1
std::cout << std::numeric_limits<float>::has_infinity << std::endl; // 1
std::cout << std::numeric_limits<double>::epsilon() << std::endl; // 2.22045e-16
std::cout << std::numeric_limits<double>::infinity() << std::endl; // inf
std::cout << std::numeric_limits<double>::digits << std::endl; // 53
std::cout << std::numeric_limits<double>::is_signed << std::endl; // 1
std::cout << std::numeric_limits<double>::has_infinity << std::endl; // 1
常量
#include <math.h>
double pi = M_PI
#include <opencv2/core/core.hpp>
double pi = CV_PI
数学函数
#include <math.h>
double pow(double x, double y); // 求x的y次方
double exp(double x); // 求e的x次幂的值
// 三角函数
std::cos(theta)
std::sin(theta)
std::atan2(y,x); // 弧度
异常
try {
throw std::runtime_error("error");
} catch (const std::exception& e) {
std::cout << "error: " << e.what();
} catch (...) {
;
}
进度条
printf("\r bar: %m.nf %% \r", value * 100);
fflush(stdout);
优化存储空间
std::vector<int> v;
v.shrink_to_fit();
分离小数的整数部分和小数部分
double timestamp = 1595318888.71254;
int64_t integer = static_cast<int64_t>(timestamp);
float decimal = timestamp - integer;
类的智能指针
#include <memory>
// 定义
class Image {
public:
using Ptr = std::shared_ptr<Image>; // 使用别名
}
// 使用
Image::Ptr image_pointer = Image::Ptr(new Image());
Image image;
Image::Ptr image_pointer = std::make_shared<Image>(image);
使用智能指针记得初始化:
#include <boost/make_shared.hpp>
pcl::PointCloud<pcl::PointXYZRGB>::Ptr point_cloud_xyzrgb_ = boost::make_shared<pcl::PointCloud<pcl::PointXYZRGB>>();
注:
typedef boost::shared_ptr<PointCloud<PointT> > Ptr;
不然会报错:Assertion `px != 0' failed
基类和派生类指针转换:
dynamic_cast和dynamic_pointer_cast
set自定义类:
#include <unordered_set>
struct EnumClassHash {
template <typename T>
std::size_t operator()(T t) const {
return static_cast<std::size_t>(t);
}
};
enum class A {
kName,
kAge,
};
std::unordered_set<A, EnumClassHash> s;
map/set元素为自定义的类:
#include <unordered_map>
struct PairHash {
template <typename T>
std::size_t operator()(T t) const {
return static_cast<std::size_t>(t.first * t.second + t.second);
}
};
std::unordered_map<std::pair<size_t, size_t>, uchar, PairHash> pixel2value;
pixel2value[{row, col}].push_back(v);
if (pixel2value.count({row, col})) {
uchar value = pixel2value.at({row, col});
}
编程习惯:
//用宏定义(不要写死)
#define IMG_WIDTH 640
#define IMG_HIGHT 480 //宏定义换行用'\'
//调试宏
#define DEBUG
#ifdef DEBUG
//do something
#endif
//注释代码块
#if 0
//do something
#endif
//类型名太长
typedef std::vector<std::vector<int>> Grid;
//函数入口进行参数检查
assert(条件/表达式);
遵循doxygen注释规范
小数后面加f
2.1(double,8字节)
2.1f(flaot,4字节)
浮点定点化
(float*2^n)>>n
for循环并行处理:
包含OpenMP的头文件:#include<omp.h>
在for循环前面加上一行:#pragma omp parallel for
C/C++编译器内置宏 __DATA__,__TIME__,__FILE__,__LINE__,可直接使用(如:std::cout << __DATA__ << std::endl; )
__DATE__:在源文件中插入当前的编译日期
__TIME__:在源文件中插入当前编译时间
__FILE__:在源文件中插入当前源文件路径及文件名
__LINE__:在源代码中插入当前源代码行号
标准库STL常用:
// 字符串相关
string::c_str //返回 C 型字符串
std::stoi //将字符串转化成带符号(Signed)整数
std::stoll //将字符串转化成带符号整数
std::to_string //将一个整数或浮点数转化成字符串
std::remove_if //删除容器中所有满足条件的元素
std::find_if //找到容器中满足条件的第一个元素的指针
// int i = 1;
// std::vector<int> v;
// auto p = std::find_if(std::begin(v), std::end(v), [&i](const int& m) { return m == i; });
// if (p != std::end(v)) {}
std::copy_if //拷贝容器中所有满足条件的元素
std::copy(start, end, std::back_inserter(container));
std::swap(a,b); //交换两个数
std::ceil() // 向上取整
std::floor() // 向下取整
std::round() // 四舍五入
// 输出流格式控制
#include <iomanip>
std::cout << std::fixed << std::setprecision(2) << timestamp << std::endl;
cin.ignore();
std::cout << std::fixed; //用一般的方式输出浮点型,例如C++程序在控制台显示大一点的数,显示的时候使用了科学计数法,使用该命令即可像一般的方式显示
std::cout.precision(2); //设置精确度为2,即小数点后保留的位数
std::stringstream ss;
ss << std::fixed << std::setprecision(2) << "(" << x << "," << y << ")";
std::string coordinate(ss.str());
// 排序
std::nth_element(_RanIt _First, _RanIt _Nth, _RanIt _Last) //该函数的作用为将迭代器指向的从_First 到 _last 之间的元素进行二分排序,以_Nth 为分界,前面都比 _Nth 小(大),后面都比之大(小);但是两段内并不有序。特别适用于找出前k个最大(最小)的元素。
std::nth_element(v.begin(), v.begin() + k, v.end());
std::sort() //默认从小到大排序
// std::sort requires a sorter which satisfies the strict weak ordering rule, see: https://stackoverflow.com/questions/18291620/why-will-stdsort-crash-if-the-comparison-function-is-not-as-operator
// 多线程中计数
std::atomic<int> cnt(0);
// 队列
#include <queue>
std::queue<int> q;
q.push() 在队尾压入新元素
q.pop() 删除队列首元素但不返回其值
q.front() 返回队首元素的值,但不删除该元素
q.back() 返回队列尾元素的值,但不删除该元素
q.empty() 如果队列为空返回true,否则返回false
q.size() 返回队列中元素的个数
清空队列(queue不支持clear操作)
q = std::queue<int>(); 直接用空的队列对象赋值
void clear(queue<int>& q) { // 使用swap,这种是最高效的,定义clear,保持STL容器的标准。
queue<int> empty;
swap(empty, q);
}
// 双端队列
// deque容器为一个给定类型的元素进行线性处理,像向量一样,它能够快速地随机访问任一个元素,并且能够高效地插入和删除容器的尾部元素。但它又与vector不同,deque支持高效插入和删除容器的头部元素,因此也叫做双端队列。
#include <deque>
std::deque<int> dq;
dq.push_front() 双端队列头部增加一个元素
dq.push_back() 双端队列尾部增加一个元素
dq.pop_front() 删除双端队列中最前一个元素
dq.pop_back() 删除双端队列中最后一个元素
dq.front() 返回首元素的引用
dq.back() 返回尾元素的引用
dq.at(int pos) 返回pos位置元素的引用
dq.empty() 判断双端队列是否为空
dq.size() 返回队列中元素的个数
dq.clear() 清空双端队列
dq.begin() 返回向量头指针,指向第一个元素
dq.end() 返回指向向量中最后一个元素下一个元素的指针
// 数组用初始化列表初始化
int array[2] = {-5, 5};
// array
#include <array>
std::array<int, 5> arr = {0, 1, 2, 3, 4};
arr[0]
arr.at(0)
arr.front()
arr.back()
arr.swap(arr2);
arr.fill(10);
std::begin(arr), std::end(arr)
// stringstream转string
std::stringstream ss;
ss << "num: " << 1 << "\n";
std::string s = ss.str();
// 注意:在C++中可以使用stringstream来很方便的进行类型转换,字符串串接,不过注意重复使用同一个stringstream对象时要先继续清空,而清空很容易想到是clear方法,而在stringstream中这个方法实际上是清空stringstream的状态(比如出错等),真正清空内容需要使用.str("")方法。
std::vector两个方法operator[]和at使用上的区别:operator[]和at的主要区别在于operator[]不做边界检查,而at会做边界检查。
由于operator[]不做边界检查, 那怕越界了也会返回一个引用,当然这个引用是错误的引用,如何不小心调用了这个引用对象的方法,会直接导致应用退出。
而由于at会做边界检查,如果越界,会抛出异常,应用可以try catch这个异常,应用还能继续运行。
结论:使用at时应使用try catch包裹住;而使用operator[]时一定要先检查一下是否越界。
std::vector vec;
vec[0],vec.at(0)
#include <algorithm>
std::vector<int> vi = {0, 1, 2};
auto iter = std::find(vi.cbegin(), vi.cend(), 0);
std::cout << *iter << std::endl;
// 替换
string::size_type pos(0);
if ((pos = str.find(old_value)) != string::npos) {
str.replace(pos, old_value.length(), new_value);
}
// stringstream转string
int i = 1;
std::stringstream ss;
ss << "#" << i;
std::string out(ss.str());
//字符串数组初始化
char *color[5]={"red","yellow","blue","white","black"};
std::map<std::string, int> m;
m.rbegin()->first;
rbegin()是C++ STL中的函数。它返回一个反向迭代器,该迭代器指向Map的最后一个元素。反向迭代器以相反的顺序进行迭代,递增迭代器意味着朝着Map的开头移动。
std::vector<int> v;
for (auto iter = v.crbegin(); iter != v.crend(); ++iter) {}
使用单例模型来进行不同文件间的变量传递:
//a.h文件
//为了避免同一个文件被include多次,C/C++中有两种方式,一种是#ifndef方式,一种是#pragma once方式
#pragma once
#ifndef _A_H_
#define _A_H_
class Singleton
{
public:
Singleton(){};
static Singleton* instance;
static Singleton* get_instance()
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
int b;
void set_b(int c){b=c;}
int get_b(){return b;]
}
#endif
//1.cpp
#include "a.h"
Singleton *ptr=Singleton::get_instance();
ptr->set_b(c);
//2.cpp
#include "a.h"
Singleton *ptr=Singleton::get_instance();
int d=ptr->get_b();
class Foo {
public:
static Foo* GetMutableFoo() {
static Foo instance;
return &instance;
}
static std::string* GetMutableDate() {
return &GetMutableFoo()->date_;
}
protected:
std::string date_;
}
boost 分隔字符串:
#include <boost/algorithm/string.hpp>
string line("test\ttest2\ttest3");
vector<string> strs;
boost::split(strs,line,boost::is_any_of("\t"));
if (boost::algorithm::to_upper_copy(strs.at(1)) == "TEST2") {}
// boost::split(strs,line,boost::is_any_of("\t"),boost::token_compress_on);
对于场景:string s = "123456",用"3","4"切分,默认情况下(boost::token_compress_off),切分结果为12,空,56,注意,这里的空不是空格。而是"3","4"之间的空。如果不想要这个空,指定boost::token_compress_on就行了。
boost::token_compress_on的意思就是说,以"3","4",切分,当出现34的时候,就把34压缩成整体,用"34"切分。
boost::algorithm::to_lower_copy()
boost::algorithm::to_upper_copy
// 利用stringstream分隔空格
std::istringstream iss(std::string("1 2 3 4"));
for (std::string s; iss >> s;) {
std::cout << s << std::endl;
}
获取home路径
#include <stdlib.h>
std::string home_path = getenv("HOME");
计算均值、方差
#include <numeric> // accumulate
void CaculateMeanAndStdev(const std::vector<double> &datas,
double *mean, double *stdev) {
double sum = std::accumulate(std::cbegin(datas), std::cend(datas), 0.0);
*mean = sum / datas.size();
double accum = 0.0;
std::for_each(std::cbegin(datas), std::cend(datas), [&](const double data) {
accum += (data - *mean) * (data - *mean);
});
*stdev = std::sqrt(accum / (datas.size() - 1));
}
C++引用,顶层const和底层const
C++的引用可以看成指针的语法,引用本身不是一个对象。int,double,float和long long等基本内置数据类型的const都是顶层const。引用的const都是底层const。
// const对象必须被初始化。
const int& b = 2; // 合法,编译器自动创建一个临时变量,然后将临时变量赋值给b
int& d = 2; // 不合法,因为不是const int&,编译器并不会创造一个临时变量来赋值
// const其实只是一个对变量写权限的设定,并不代表某个变量永远不可能被修改。
int temp = 2;
const int& p = temp; // 只是不能通过p改变temp,但是改变temp的值会改变p的值!
temp = 3; // p只是temp的别名
// 指针既可以是顶层const也可以是底层const,也可以同时是两种const。
int *p; // 无const
const int *p; // 底层const,不能改变p所指向的值,能改变p的值
int* const p; // 顶层const,不能改变p的值,能改变p指向的值(典型的有clas中的this指针)
const int* const p; // 两种const,既不能改变p指向的值也不能改变p的值
// 底层const是不可忽略的。当执行对象的拷贝过程中(赋值操作,函数的值传递)时,如果被拷贝对象拥有底层const资格,则拷贝对象必须拥有相同的底层const资格。如果拷贝对象拥有底层const,则无所谓被拷贝对象是否有const。对于一个既是顶层const又是底层const对象来说,无所谓它是否为顶层const,只要关注它的底层const就行了。
const int pi = 3.14;
int& c = pi; // 不合法,c的值可以改变,但pi是const,与程序员本意矛盾
const int p = 5; // 无外部链接
extern const int temp = 5; // 有外部链接的const 定义,在其他文件使用需声明
设计类
a.h
#pragma once
#include <memory>
namespace foo {
namespace bar {
// class A
class A {
public:
typedef std::shared_ptr<A> Ptr;
typedef std::shared_ptr<A const> ConstPtr; // 顶层const,const修饰的是指针,指针本身不能改变,但指针指向的A可以变
using Z = X::Y::Z;
A() {}
A(const A &) = delete;
virtual bool Read(const std::string& file_name) = 0;
void func(Z z);
virtual ~A() = default;
private:
template <typename T>
void func(T t);
}; // class A
} // namespace bar
} // namespace foo
a.cc
#include "a.h"
namespace foo {
namespace bar {
namespace bar = foo::bar;
using namespace B
using M::N::f;
A::A() {}
void A::func(Z z) {
f();
}
} // namespace bar
} // namespace foo
if ((a & 3) == 0) {}