前言
本技术文档是个人知识的总结再次整理发布出来共大家共同进步和修正。如果有发现不对之处请联系我更正,共同学习和进步。
文档不是详细开发教程所以不适合入门。
C++基础
官方在线文档
https://zh.cppreference.com/w/cpp
关键字(重要)
decltype
decltype (int) a = {5}; 类型检测
typeid
typeid(int) == typeid(5); 获取类型名字
auto
auto itr = m.find(); 类型推导
变量
boot true/false
char 1byte
short 2byte
int 4byte
long 32位系统 4byte, 64位系统8byte
long long 8byte
flot 4byte
double 8byte
整形变量都可以增加 unsigned 表示最高位不充当符号位
指针 引用
int *ip; /* 一个整型的指针 */
double *dp; /* 一个 double 型的指针 */
float *fp; /* 一个浮点型的指针 */
char *ch; /* 一个字符型的指针 */
int& r = i;
double& s = d;
const string &str = "string"
//const类型引用可以引用右值
//函数指针
void (*fun)(int, int)
//类函数指针
//静态成员函数指针
void (*funStatic)() = &ClassName::staticFun;
//成员方法函数指针
void (ClassName::*funNoStatic)() = &ClassName::noStaticFun;
//使用
funStatic();
(obj->*funNoStatic)();
友元函数 友元类
权访问类的所有私有(private)成员和保护(protected)成员
class Box
{
double width;
public:
double length;
friend void printWidth( Box box );
void setWidth( double wid );
};
friend class ClassTwo;
命名空间
// 第一个命名空间
namespace first_space{
void func(){
cout << "Inside first_space" << endl;
}
}
int main ()
{
// 调用第一个命名空间中的函数
first_space::func();
return 0;
}
//引用命名空间
using namespace first_space;
int main ()
{
// 调用第一个命名空间中的函数
func();
return 0;
}
c++11
using 定义别名 using fun = void (*)(); 同typedef
using /*使用外部构造*/
class Base
{
public:
Base(int va) {}
};
class Derived :public Base
{
public:
//使用继承构造函数
using Base::Base
}
//模板别名
template <typename T>
using ArenaQueue = std::queue<T, ArenaDeque<T>>;
类
//说道类我要说两句。个人感觉类是一个在程序里面实现高内聚、低耦合的很简洁的方式。
//public 类外部可以访问
//protected 子类可以访问,外部不能访问
//private 只能本类访问
//delete 删除该类型函数,程序中不可调用,调用会报错
//defaulte 指定默认构造函数
//explicite 防止类构造函数的隐式自动转换
//final 终结虚函数传递, 子类不能再实现该函数
//override 说明该函数式覆盖函数,父类必须有其相同定义
class FirstClass {
public:
int getLength( void );
FirstClass (bool ) = delete;
FirstClass (char ) = default;
explicite FirstClass ( int len ); // 简单的构造函数
FirstClass ( const Line &obj); // 拷贝构造函数
~FirstClass (); // 析构函数
virtual void vir_fun(); //虚函数 类多态使用
virtual int area() = 0; //不用给出实现 派生类必须实现
protected:
private:
}
//继承
//public 保持之类的访问属性 即: public protected private
//protected 改变继承的 public访问控制位 protected,其他不变
//private 改变继承的 protected public为 private
//子类只能访问父类 protected public的成员不能访问 private的成员
class ChildClass : public FirstClass{
using FirstClass::FirstClass;
final void vir_fun() {}
final int area() {}
override int getLength() {}
}
//多态
FirstClass *firstClass = (new ChildClass(5));
firstClass->vir_fun() //执行ChildClass类的函数
重载操作符
可以被重载的操作符:new,new[],delete,delete[],+,-,*,/,%,^,&,|,~,!,=,<,>,+=,<<,>>,<<=,>>=,++,!=,<=,>=,&&,||,++,--,->*,->,(),[]
不可以被重载的操作符:. .* :: ?:
class UPInt { // "unlimited precision int"
public:
UPInt& operator++(); // ++ 前缀
const UPInt operator++(int); // ++ 后缀
UPInt& operator--(); // -- 前缀
const UPInt operator--(int); // -- 后缀
UPInt& operator+=(int); // += 操作符,UPInts
// 与ints 相运算
...
};
模板
// 函数模板
template <class type> ret-type func-name(parameter list)
{
// 函数的主体
}
#include <iostream>
#include <string>
using namespace std;
template <typename T>
inline T const& Max (T const& a, T const& b)
{
return a < b ? b:a;
}
int main ()
{
int i = 39;
int j = 20;
cout << "Max(i, j): " << Max(i, j) << endl;
double f1 = 13.5;
double f2 = 20.7;
cout << "Max(f1, f2): " << Max(f1, f2) << endl;
string s1 = "Hello";
string s2 = "World";
cout << "Max(s1, s2): " << Max(s1, s2) << endl;
return 0;
}
//类模板
template <class type> class class-name {
.
.
.
}
template <class T>
class Stack {
private:
vector<T> elems; // 元素
public:
void push(T const&); // 入栈
void pop(); // 出栈
T top() const; // 返回栈顶元素
bool empty() const{ // 如果为空则返回真。
return elems.empty();
}
};
template <class T>
void Stack<T>::push (T const& elem)
{
// 追加传入元素的副本
elems.push_back(elem);
}
template <class T>
void Stack<T>::pop ()
{
if (elems.empty()) {
throw out_of_range("Stack<>::pop(): empty stack");
}
// 删除最后一个元素
elems.pop_back();
}
template <class T>
T Stack<T>::top () const
{
if (elems.empty()) {
throw out_of_range("Stack<>::top(): empty stack");
}
// 返回最后一个元素的副本
return elems.back();
}
//模板默认参数
template <class T, class V = 5>
auto自动类型推导
template<typename T, typename K>
auto add(T a, K b) {
return a + b;
}
auto a = add(1, 2); // int add(int, int)
auto b = add(1, 2.2); // double add(int, double)
foreach遍历
//以前
for (std::vector<int>::const_iterator itr = v.begin(); itr != v.end(); ++itr) {
std::cout << *itr << std::endl;
}
//现在
std::vector<string> v = { "a", "b" };
for (auto& s : v) {
std::cout << s << std::endl;
}
//遍历
std::vector<int> {1, 2, 3, 4};
//不传入参数
for_each(v.begin(), v.end(), [](int i){
count<<i<<endl;
})
//传入参数
for_each(v.begin(), v.end(), [](int i, std::string = "Element"){
})
初始化列表 Initializer List 和 统一的初始化方法
所有STL容器都支持初始化列表,如下:
std::vector<int> v = { 1, 2, 3 };
std::list<int> l = { 1, 2, 3 };
std::set<int> s = { 1, 2, 3 };
std::map<int, std::string> m = { {1, "a"}, {2, "b"} };
#include <initializer_list>
class A {
public:
B(const std::initializer_list<int>& items)
: m_items(items)
{}
private:
std::vector<int> m_items;
};
//统一初始化
A a1 = { 1, 2, 3 };
// 或者
A a2{ 1, 2, 3 };
强类型枚举 enum class
enum class Direction {
Left, Right
};
enum class Answer {
Right, Wrong
};
auto a = Direction::Left;
auto b = Answer::Right;
if (a == b)
std::cout << "a == b" << std::endl;
else
std::cout << "a != b" << std::endl;
静态断言 static assert
static_assert( size_of(int) == 4 );
构造函数的相互调用 delegating constructor
class A {
public:
A(int x, int y, const std::string& name) : x(x), y(y), name(name) {
if (x < 0 || y < 0)
throw std::runtime_error("invalid coordination");
if (name.empty())
throw std::runtime_error("empty name");
}
A(int x, int y) : A(x, y, "A")
{}
A() : A(0, 0)
{}
private:
int x;
int y;
std::string name;
};
constexpr常量表达式
constexpr 用于向编译器指出,常量表达式
constexpr int a = 20; /* 20 是常量表达式 */
constexpr int b = a + 2; /* a + 2 是常量表达式 */
override 和 final 虚函数
override 修饰函数, 函数必须覆盖父类的函数,
final 打断virtual的传播 如果一个虚函数被final修饰所有的子类都不能覆盖该函数,
默认构造函数 default
class A {
public:
A(int i) {}
A() = default;
};
删除构造函数 delete
class A {
public:
A() = delete;
};
字符串字面量
const char* a = "string a";
const char* b = u8"string b"; // UTF-8
const char16_t* c = u"string c"; // UTF-16
const char32_t* d = U"string d"; // UTF-32
const char* e = R"(string e1 "\\
stirng e2)"; // raw string
std::cout << a << std::endl;
std::cout << b << std::endl;
std::cout << c << std::endl;
std::cout << d << std::endl;
std::cout << e << std::endl;
R原始字符
auto xml = R"(<root>
<item value="1">
<item value="2">
</root>)";
lambad
Lambda 表达式: Lambda表达式就是匿名函数,。
[capture](parameter)mutable -> return-type{state}
capture 是捕捉列表。
parameter 是参数列表,就是函数的那些传入变量。
mutable 这个后面再介绍。
return-type 返回值的类型,如果返回值明确,也可以省略。
state 是函数体
Lambad 表达式引入符:
[] 不捕获任何局部变量
[=] 以传值的方式捕获外部变量
[&] 以引用的方式捕获外部变量
[x, &y] x传值捕获, y引用捕获
[=, &x] x引用捕获, 其他传值捕获
[&, x] x传值捕获, 其它引用捕获
[a, &str]->QString{} 传值捕获a, 引用捕获str, 返回值是QString。
模板可变参数
//函数 变长参数 模板
template <typename T>
void fun(const T& t){
cout << t << '\n';
}
template <typename T, typename ... Args>
void fun(const T& t, Args ... args){
cout << t << ',';
fun(args...);//递归解决,利用模板推导机制,每次取出第一个,缩短参数包的大小。
}
sizeof...(args) 长度
//类模板
template<typename T1, typename T2> class Car{};
template<typename... A> class BMW : public Car<A...>{};
BMW<int, char> car;
右值引用 移动语意
右值引用解决了左值引用无法传递临时对象和常引用传递的对象只读的问题. 右值引用允许传递一个可变的临时对象引用.
移动构造使用移动而非赋值语义完成构造过程, 主要用于解决函数返回值时的大量深拷贝开销.
#include <utility>
int main(void)
{
int a = 30;
int &b = a;
//int &&c = a; /* error 右值引用不能绑定到左值 */
//int &d = a * 2; /* erro a * 2 是一个右值 */
const int e = a * 2; /* const 的引用可以绑定到一个右值 */
int && f = a * 2; /* 右值引用可以绑定到右值 */
//int && g = f; /* error 不能将一个右值引用绑定到一个变量上, 即使这个变量是右值引用类型也不行 */
int && h = std::move(f); /* ok */
return 0;
}
int r1 = 20;
int &&r2 = std::move(r1);
调用move就意味着承诺: 除了对r1赋值或销毁它以外, 将不能再使用它.
正则表达式 regex
regex | 表示有一个正则表达式类 |
regex_match | 将一个字符序列与一个正则表达式匹配 |
regex_search | 寻找第一个与正则表达式匹配的子序列 |
regex_replace | 使用给定格式替换一个正则表达式 |
sregex_iterator | 迭代适配器, 调用regex_search来遍历一个string中所有匹配的子串 |
smatch | 容器类, 保存在string中搜索的结果 |
ssub_match | string中匹配的子表达式的结果 |
#include <iostream>
#include <string>
#include <regex>
int main(void)
{
std::string str1[] = {"foo.txt", "b.txt", "abcd.txt", "txt", "a0.txt", "AAA.txt", "wt.txt"};
std::regex txtRegex("[a-z]+\\.txt");
std::regex baseRegex("([a-z]+)\\.txt");
std::smatch baseMatch;
for (const auto &s : str1)
{
if (std::regex_match(s, baseMatch, baseRegex))
{
std::cout << "baseMatch[0]: " << baseMatch[0] << std::endl;
std::cout << "baseMatch.szie: " << baseMatch.size() << std::endl;
if (baseMatch.size() == 2)
{
std::string str = baseMatch[0].str();
std::string base = baseMatch[1].str();
std::cout << "str: " << str << std::endl;
std::cout << s << " : " << base << std::endl;
}
}
}
return 0;
}
智能指针
//std::auto_ptr 指针对象析构 指向对象自动释放
auto item = auto_ptr<Item> (new Item());
auto_ptr复制构造函数中把真是引用的内存指针进行的转移 可能产生野指针
//std::shared_ptr
shared_ptr自动销毁所管理的对象
std::shared_ptr 是一种智能指针,它能够记录多少个 shared_ptr 共同指向一个对象,从而消除显示的调用 delete,当引用计数变为零的时候就会将对象自动删除。
std::shared_ptr 可以通过 get() 方法来获取原始指针,通过 reset() 来减少一个引用计数,并通过get_count()来查看一个对象的引用计数。
shared_ptr<int> p1;
//被初始化成为一个空指针
shared_ptr<int> p2 (new int(4));
//指向一个值是4的int类型数据
shared_ptr<int> p3 = new int(4);
//错误,必须直接初始化
shared_ptr<T> p;
//空智能指针,可指向类型是T的对象
if(p)
//如果p指向一个对象,则是true
(*p)
//解引用获取指针所指向的对象
p -> number == (*p).number;
p.get();
//返回p中保存的指针
swap(p,q);
//交换p q指针
p.swap(q);
//交换p,q指针
make_shared<T>(args)
//返回一个shared_ptr的对象,指向一个动态类型分配为T的对象,用args初始化这个T对象
shared_ptr<T> p(q)
//p 是q的拷贝,q的计数器++,这个的使用前提是q的类型能够转化成是T*
shared_pts<T> p(q,d)
//p是q的拷贝,p将用可调用对象d代替delete
//上面这个我其实没懂,也没有查出来这个的意思
p =q;
//p的引用计数-1,q的+1,p为零释放所管理的内存
p.unique();
//判断引用计数是否是1,是,返回true
p.use_count();
//返回和p共享对象的智能指针数量
p.reset();
p.reset(q);
p.reset(q,d);
//reset()重新设置 新的指想对象
//std::weak_ptr
weak_ptr是一种不控制所指向对象生存期的智能指针,指向shared_ptr管理的对象,但是不影响shared_ptr的引用计数。它像shared_ptr的助手,一旦最后一个shared_ptr被销毁,对象就被释放,weak_ptr不影响这个过程。
weak_ptr是为配合shared_ptr而引入的一种智能指针来协助shared_ptr工作,它可以从一个shared_ptr或另一个weak_ptr对象构造,它的构造和析构不会引起引用计数的增加或减少。没有重载 * 和 -> 但我们可以通过lock来获得一个shared_ptr对象来对资源进行使用,如果引用的资源已经释放,lock()函数将返回一个存储空指针的shared_ptr。 expired函数用来判断资源是否失效。
weak_ptr的使用更为复杂一点,它可以指向shared_ptr指针指向的对象内存,却并不拥有该内存,而使用weak_ptr成员lock,则可返回其指向内存的一个share_ptr对象,且在所指对象内存已经无效时,返回指针空值nullptr。
注意:weak_ptr并不拥有资源的所有权,所以不能直接使用资源。
可以从一个weak_ptr构造一个shared_ptr以取得共享资源的所有权。
weak_ptr<T> w(sp);
//定义一个和shared_ptr sp指向相同对象的weak_ptr w,T必须能转化成sp指向的类型
w = p;
//p是shared_ptr或者weak_ptr,w和p共享对象
w.reset();
//w置为空
w.use_count();
//计算与w共享对象的shared_ptr个数
w.expired();
//w.use_count()为0,返回true
w.lock();
//w.expired()为true,返回空shared_ptr,否则返回w指向对象的shared_ptr
share_ptr<X> pnew(w.lock())
//std::unique_ptr
unique_ptr 不共享它的指针。它无法复制到其他 unique_ptr,无法通过值传递到函数,也无法用于需要副本的任何标准模板库 (STL) 算法。只能移动unique_ptr。
可以使用 移动语义转移所有权
unique_ptr<int> pInt(new int(5));
unique_ptr<int> pInt2 = std::move(pInt); // 转移所有权 转换为右值
函数可以返回 unique_ptr 类似移动语义 函数返回值是右值
unique_ptr<int> clone(int p)
{
unique_ptr<int> pInt(new int(p));
return pInt; // 返回unique_ptr
}
int main() {
int p = 5;
unique_ptr<int> ret = clone(p);
cout << *ret << endl;
}
//示例代码
#include <memory>
#include <iostream>
#include <utility>
class Foo{
public:
Foo() = default;
Foo(int a):_a(a) {}
~Foo() {}
int get_a(){
return _a;
}
void set_a(int a) {
_a = a;
}
private:
int _a;
};
std::unique_ptr<Foo> change_a(std::unique_ptr<Foo> f)
{
f->set_a(10);
return f;
}
int main()
{
// std::make_unique是c++14才有
std::unique_ptr<Foo> pf = std::make_unique<Foo>(10);
// unique_ptr的复制构造函数和拷贝构造函数是删除了的,这样保证了对象独占,如果不是独占,那么跟shared_ptr
// 就是一样的功能了。
// std::unique_ptr<Foo> pf1 = pf; // compile error
// 按值传参,会有拷贝问题,同上
// auto p = change_a(pf); //compile error
auto p = change_a(std::move(pf));
std::cout << "get_a = " << p->get_a() << std::endl;
if(!pf)
{
std::cout << "pf is nullptr" << std::endl;
}
//owner transfer from function
std::unique_ptr<Foo> pf2 = std::make_unique<Foo>(11);
std::unique_ptr<Foo> p2 = change_a(std::move(pf2));
std::cout << "get_a = " << p2->get_a() << std::endl;
if(!pf2)
{
std::cout << "pf2 is nullptr" << std::endl;
}
//使用reset
pf2.reset(new Foo(12));
std::cout << "pf2 is not null: " << pf2->get_a() << std::endl;
//release获取原始指针
Foo* ff = pf2.release();
if(!pf2)
{
std::cout << "pf2 is nullptr" << std::endl;
}
std::cout << "ff is not null: " << ff->get_a() << std::endl;
return 0;
}
智能指针数组删除
//! shared_ptr
std::shared_ptr<int> p(new int[10],std::default_delete<int[]>());
//! unique_ptr
std::unique_ptr<int[]> p(new int[10]);//ok
类型别名:using
对于冗长或复杂的标识符,如果能够创建其别名将很方便。以前,C++为此提供了typedef:
typedef std::vector<std::string>::iterator itType;
C++11提供了另一种创建别名的语法:
using itType = std::vector<std::string>::iterator;
两者差别在于,新语法也可用于模板部分具体化,当typedef不能:
template<tepename T>
using arr12 = std::array<T, int>;//template for multiple aliases
上述语句具体化模板array<T, int>(将参数int设置文12)。例如对于下述声明:
std::array<double, 12> a1;
std::array<std::string, 12> a2;
可将它们替换为如下声明:
arr12<double> a1;
arr12<std::string> a2;
外部模板
C++11引入了外部模板声明, 就像外部数据声明一样. C++03用下面的语法迫使编译器实例化一个模板:
template class std::vector<MyClass>;
而C++11提供下面的语法告诉编译器在当前编译单元中不要实例化这个模板:
extern template class std::vector<MyClass>;
C++标准库
文档库
http://www.cplusplus.com/reference/
随机数
c随机数
std::srand(std::time(nullptr));
int randomValue = std::rand();
c++随机数
1. 非确定随机数生成器
std::random_device rd;
count<< rd();
2. 种子序列, 以给定小种子或分布凄惨的初始种子序列,播种大量随机数引擎或播种要求大量熵的随机数
std::seed_seq seq {1, 3, 3, 5};
std::vector<int> values(5);
seq::generate(std::begin(values), std::end(values));
3. 随机数生成引擎
linear_congruential_engine
mersenne_twister_engine
subtract_with_carry_engine
尽量使用随机数生成器
4. 随机数生成器
minstd_rand0(C++11)
minstd_rand(C++11)
mt19937(C++11)
mt19937_64(C++11)
ranlux24_base(C++11)
ranlux48(C++11)
default_random_engine(C++11)
5. 分布
uniform_int_distribution (C++11)产生在一个范围上均匀分布的整数值(类模板)
uniform_real_distribution(C++11)产生在一个范围上均匀分布的实数值(类模板)
6 //使用
std::random_device rd;
std::default_random_engine(rd());
std::uniform_int_distribution<int> dis(1, 6);
dis(); //获取1 - 6整数
type:可以是 short 、 int 、 long 、 long long 、 unsigned short 、 unsigned int 、 unsigned long 或 unsigned long long
//
std::random_device rd; // 将用于获得随机数引擎的种子
std::default_random_engine gen(rd()); // 以 rd() 播种的标准 mersenne_twister_engine
std::uniform_real_distribution<double> dis(1, 2);
for (int n = 0; n < 10; ++n) {
// 用 dis 变换 gen 生成的随机 unsigned int 为 [1, 2) 中的 double
std::cout << dis(gen) << ' '; // 每次调用 dis(gen) 都生成新的随机 double
}
多线程和并发
多线程
std::async() 和 std::future
async启动策略:
1. std::launch::async 执行后立刻创建对应执行线程
2. std::launch::deferred 当其他线程调用get获取状态时,执行等待
3. std::launch::async||std::launch::deferred 默认策略
//异步策略启动
std::future<void> future1 = std::async(std::launch::async, []{
std::this_thread::sleep_for(std::chrono::secodes(1));
})
对于异步策略,不需要wait 或者 get 来等线程结束,异步策略会等待线程结束
//延时策略
std::future<void> future1 = std::async(std::launch::deferred, []{
std::this_thread::sleep_for(std::chrono::secodes(1));
})
//注意 async返回值如果不使用会造成阻塞 直到fun完成
future1.get();
future1.wait();
//共享 Future
std::shared_future<int> f = std::async([]{});
auto f = async([]{}).share();
//异常
try {
int number = f.get();
} catch(const exception &e) {
cerr<<this_thread::get_id<<e.what();
}
锁
std::mutex myMutex;
线程安全函数
int peopleNumber = 10;
int getPeopleNumber()
{
static std::mutex myMytex;
myMutex.lock();
++val;
myMutex.unlock();
}
//析构自动 释放 unlock
std::lock_guard<std::mutex> lg(valMutex);
//递归锁 同一线程多次加锁
std::recursive_mutex recursive_mutex;
void firstMutex()
{
std::lock_guard<std::recursive_mutex> lg_recursive_mutex(mutex);
}
void secondMutex()
{
std::lock_guard<std::recursive_mutex> lg_recursive_mutex(mutex);
firstMutex();
}
// 析构自动 释放 unlock 但是可以临时加锁 解锁
std::unique_lock<std::mutex> guard(_mu);
//do something 1
guard.unlock(); //临时解锁
//do something 2
guard.lock(); //继续上锁
std::lock(mutex1, mutex2) //阻塞等待
std::try_lock(mutex1, mutex2) // -1成功 否则返回失败锁的索引
条件变量
#include <mutex>
#include <condition_variable>
std::mutex readyMutex;
std::contition_variable readyCondVar;
//唤醒线程
readyCoundVar.notify_one();
readyCoundVar.notify_all();
//等待唤醒线程
std::unique_lock<std::mutex> l(readyMutex);
readyCondVar.wait(l);
wait 导致当前线程阻塞直至条件变量被通知,或虚假唤醒发生,可选地循环直至满足某谓词
原子操作
#include <atomic>
std::atomic<bool> readyFlag(false);
readyFlag.store(true) //赋值
readyFlag.load() //取值
C++第三方库
google glog日志库
安装
sudo apt install libgflags-dev
sudo apt install libgoogle-glog-dev
基本配置
配置
google-glog 日志分四类:
1. INFO,
2. WARNING,
3. ERROR 和
4. FATAL,对于 FATAL ,程序输出日志后将退出
设置是否输出到标准错误,默认否,即输出到文件
命令行参数配置
$ --logtostderr=1 # 1,true 和 yes(大小写均可)意思相同
$ --logtostderr=0 # 0,false 和 no (大小写均可)意思相同
程序中配置
FLAGS_logtostderr = 1; # 1 和 true 意思相同
FLAGS_logtostderr = 0; # 0 和 false 意思相同
设置将指定等级及其以上等级的日志输出到标准错误,默认为 ERROR
配置中 0 表示 INFO,1 表示 WARNING,2 表示 ERROR,3 表示 FATAL
命令行参数配置
$ --stderrthreshold=2
程序中配置
FLAGS_stderrthreshold = 2;
设置输出指定等级及其以上等级的日志,默认为 INFO
配置中 0 表示 INFO,1 表示 WARNING,2 表示 ERROR,3 表示 FATAL
命令行参数配置
$ --minloglevel=0
程序内配置
FLAGS_minloglevel = 0;
设置日志目录,默认为 /tmp
命令行参数配置
$ --log_dir=
程序内配置
FLAGS_log_dir =
设置 log 是否使用颜色,默认不使用
命令行参数配置
$ --colorlogtostderr=1 # 1,true 和 yes(大小写均可)意思相同
$ --colorlogtostderr=0 # 0,false 和 no (大小写均可)意思相同
函数内配置
FLAGS_colorlogtostderr = 1; # 1 和 true 意思相同
FLAGS_colorlogtostderr = 1; # 0 和 false 意思相同
使用
添加需要的头文件
#include <glog/logging.h>
设置使用文档(可选)
google::SetUsageMessage("How to use");
设置版本(可选)
google::SetVersionString("1.0.0");
配置 LOG
FLAGS_logtostderr = true; // 设置 log 输出到标准错误
FLAGS_colorlogtostderr = true; // 设置 log 使用颜色
解析命令行
google::ParseCommandLineFlags(&argc, &argv, true);
初始化 google-glog
google::InitGoogleLogging(argv[0]);
使用 google-glog
LOG(INFO) << "This is INFO message."
编译
$ g++ main.cc -lgflags -glog
常用的日志宏
简单的输出日志
LOG(INFO) << "message";
按条件输出日志
LOG_IF(INFO, a > 0) << "message";
检查某个条件是否成立
CHECK(a > 0) << "message";
检测两个值是否相等
CHECK_EQ(a, b) << "message";
坚持两个值是否不相等
CHECK_NE(a, b) << "message";
检测是否为空指针,将返回该指针
CHECK_NOTNULL(p);
打印系统错误
PLOG();
PLOG_IF();
PCHECK();
FLAGS
1.Flag_xxx
FLAGS_logtostderr = false; //是否将所有日志输出到stderr,而非文件
FLAGS_alsologtostderr = false; //日志记录到文件的同时输出到strerr
FLAGS_colorlogtostderr = false; //将彩色日志输出到stderr
FLAGS_drop_log_memory = true; //日志写到文件的时候删除其在内存中的buf
FLAGS_alsologtoemail = ""; //日志记录到文件的同时发送邮件到指定邮件地址
FLAGS_log_prefix = true; //每行log加前缀
FLAGS_minloglevel = google::INFO; //日志最低记录等级
FLAGS_logbuflevel = google::INFO; //缓存日志的最低等级 , -1表示不缓存,0表示只缓存google::INFO
FLAGS_logbufsecs = 30; //日志最多缓存的秒数
FLAGS_logemaillevel = 999; //日志发送邮件的最低等级 , 0表示发送全部 , 3表示只发送google::FATAL
FLAGS_logmailer = "/bin/mail" ; //邮件发送的用户
FLAGS_log_dir = ""; //设置日志文件输出目录
FLAGS_log_link = ""; //给日志目录的文件添加额外的链接
FLAGS_max_log_size = 1800; //最大日志大小(MB), 如果设置为0将默认为1
FLAGS_stop_logging_if_full_disk = false;//磁盘满停止记录日志
FLAGS_log_backtrace_at = ""; //当记录"文件名:行号"的日志的时候触发一个backtrace
FLAGS_v = 0; //记录所有 VLOG(m)中 m <= 这个值的自定义log , 这个标签会被 FLAGS_vmodule 覆盖
FLAGS_vmodule = ""; //分模块(文件)设置
使用实例
#include <iostream>
#include <glog/logging.h>
using namespace std;
int main(int argc, char *argv[]) {
FLAGS_log_dir = "./logs";
FLAGS_colorlogtostderr = true; // 设置 log 使用颜色
FLAGS_logtostderr = true;
system("mkdir -p ../logs");
google::ParseCommandLineFlags(&argc, &argv, true); //解析命令行
google::InitGoogleLogging(argv[0]); //初始化 google-glog
LOG(INFO)<< "hello , google"<<endl;
SYSLOG(INFO)<<"sys log"<<endl;
PLOG(INFO)<<"plog"<<endl;
}
//!编译
LIBRARIES := -lglog -lgflags
//!
./bin/main --logtostderr=0 //输出到文件
./bin/main --logtostderr=1 //输出到终端