C++笔记

计时:

// 方法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) {}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值