《总之,好记性不如烂笔头!把你遗忘的都记下来吧!》
C++基础语法拾遗 引用的本质 引用可以理解为变量的别名,不能超脱变量而存在。
在底层上,引用变量由指针按照指针常量的方式实现。
(1)在内存中都是占用4个字节(32bits系统中)的存储空间,存放的都是被引用对象的地址,都必须在定义的同时进行初始化。
(2)指针常量本身(以p为例)允许寻址,即&p返回指针常量(常变量)本身的地址,被引用对象用*p表示;引用变量本身(以r为例)不允许寻址,&r返回的是被引用对象的地址,而不是变量r的地址(r的地址由编译器掌握,程序员无法直接对它进行存取),被引用对象直接用r表示。
(3)凡是使用了引用变量的代码,都可以转换成使用指针常量的对应形式的代码,只不过书写形式上要繁琐一些。反过来,由于对引用变量使用方式上的限制,使用指针常量能够实现的功能,却不一定能够用引用来实现。 例如,下面的代码是合法的:
1 2 int i=5 , j=6 ;int * const array[]={&i,&j};
而如下代码是非法的:
1 2 int i=5 , j=6 ;int & array[]={i,j};
也就是说,数组元素允许是指针常量,却不允许是引用。C++语言机制如此规定,原因是避免C++语法变得过于晦涩。假如定义一个“引用的数组”,那么array[0]=8;这条语句该如何理解?是将数组元素array[0]本身的值变成8呢,还是将array[0]所引用的对象的值变成8呢?
static标识符
可调用对象
定义一个函数指针,它的名字是create_object,指向返回值为void*,参数为void的函数
1 void * (*create_object)(void );
由于使用了typedef ,它定义一个指向返回值为void*,接收参数为void的函数指针类型 ,并将它重命名为create_object
1 typedef void * (*create_object)(void );
回调函数 回调函数是一个函数指针,通过参数传递给另一个函数。当这个参数函数在某个特定的事件或条件满足时被调用时,这个函数就是回调函数。回调函数广泛应用于事件驱动编程、异步操作和处理某些操作完成后的后续步骤。
为什么要使用回调:
解耦逻辑 : 回调函数可以将代码逻辑分离,使得代码更模块化、更易读、更易维护。
异步操作 : 在处理异步操作(如I/O操作、网络请求)时,回调函数可以在操作完成后执行特定的逻辑,而无需阻塞主程序 。
事件处理 : 在事件驱动编程中,回调函数用于响应特定的事件(如按钮点击、鼠标移动) 。
可复用性 : 回调函数可以传递不同的函数,从而实现不同的行为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <iostream> typedef void (*CallbackFunc) (int ) ;void performOperation (int x, CallbackFunc callback) { std::cout << "Performing operation with value: " << x << std::endl; callback (x); }void myCallback (int x) { std::cout << "Callback called with value: " << x << std::endl; }int main () { performOperation (5 , myCallback); return 0 ; }
智能指针 std::shared_ptr std::unique_ptr std::weak_ptr std::make_shared C++11使用make_shared的优势和劣势 - 南哥的天下 - 博客园 (cnblogs.com)
std::tuple 区分初始化和赋值 初始化 是创建对象并为其设置初始值的过程 ,在这个过程中构造函数,(包括拷贝构造函数和移动构造函数)会被调用。共有两种初始化的方式
1、直接初始化,利用()
2、复制(拷贝)初始化,利用=
赋值 是已经存在 的对象赋新值 的过程,在这个过程中赋值运算符,(包括拷贝赋值运算符和移动赋值运算符)会被调用。
1 2 3 4 a = b; a = std::move (b);
拷贝构造产生的情况
使用=定义变量的时候;
将对象作为实参传递给一个非引用类型的实参;
从返回类型为非引用类型的函数中返回一个对象;
使用花括号列表初始化数组元素或者聚合类成员;
拷贝构造参数为 const &的原因:
对于const类型的参数也能传递
在没有移动构造的情况下能够使用拷贝构造
一个类的拷贝构造被删除了,那么其移动构造也不会存在
隐式类型转换 1、算数类型隐式转化:
在条件中。非布尔值转化为布尔值
整型提升
有符号转化为无符号,但不改变内存
2、类类型隐式转换:
如果构造函数只接受一个参数或者接受一个参数其余参数都存在默认值,它实际定义了此类类型隐式类型转换机制,这种构造函数叫做:转换构造函数 。
1 2 3 4 5 6 7 8 9 10 11 12 13 class Rational {public : Rational (int numerator = 0 , int denominator = 1 ) : numerator_ (numerator), denominator_ (denominator) { if (denominator_ == 0 ) { throw std::invalid_argument ("Denominator cannot be zero" ); } }private : int numerator_; int denominator_; };
类型转化运算符 通过实现类型转化运算符来实现,可以实现隐式类型转化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <iostream> #include <cmath> class Complex {public : Complex (double r, double i) : real (r), imag (i) {} operator double () const { return std::sqrt (real * real + imag * imag); }private : double real; double imag; };int main () { Complex c (3.0 , 4.0 ) ; double magnitude = c; std::cout << "Magnitude: " << magnitude << std::endl; return 0 ; }
operator new / delete explicit 通过将构造函数声明为explicit加以阻止隐式转化,关键字explicit只对一个实参的构造函数有效,需要多个实参的构造函数不能用于执行隐式转换(参数存在默认值的情况除外),也无需将它们声明为explicit的。
1 2 explicit Rational (int numerator = 0 , int denominator = 1 ) ;
再次尝试进行隐式转化,可以看到被禁止了。
发生隐式转化的情况是我们使用了拷贝初始化,使用explicit禁止后靠被初始化就失效了
1 2 Rational r1 (5 ) ; Rational r2 = 5 ;
auto自动类型推导 引用 auto &,捕获指针 auto *
inline 内联函数的定义和实现必须在同一个文件下,也就是说inline函数定义放在头文件中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class MyClass {public : MyClass (); ~MyClass (); };inline MyClass::MyClass () { }inline MyClass::~MyClass () { }#include "MyClass.h" void func () { MyClass obj; }
类的继承 重载、隐藏、重写(覆盖) 1、重载 :从overload翻译而来,代表具有不同参数列表的同名函数,根据参数列表选择调用哪个函数,不关心函数返回类型。
1 2 3 4 5 6 int test () ;int test (int a) ; int test (int a,double b) ; int test (double a,int a) ;int test (string s) const ; ...
2、隐藏 :对于存在继承关系,且派生类重新定义了基类的方法就会发生隐藏。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <iostream> using namespace std;class Basic {public : void Show () { cout << "Basic:Show(void)\n" ; } virtual void Show (int a) { cout << "Basic:Show(int):" << a << endl; } };class Derive : public Basic {public : void Show () { cout << "Derive:Show(void)\n" ; } };int main (void ) { Derive d; d.Show (); return 0 ; }
3、重写 :从override翻译而来,又叫覆盖,指派生类中存在重新定义的与基类完全相同的同名函数,未被重写的同名函数将被隐藏,导致派生类不能访问,重写多出现在虚函数中,不是虚函数也可以发生重写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <iostream> using namespace std;class Basic {public : void Show () { cout << "Basic:Show(void)\n" ; } virtual void Show (double b, int a) { cout << "Basic:virtual Show(double,int):" << b << "," << a << endl; } };class Derive : public Basic {public : void Show () { cout << "Derive:Show(void)\n" ; } virtual void Show (double b, int a) { cout << "Derive:virtual Show(double,int):" << b << "," << a << endl; } };int main (void ) { Basic *p = new Derive; p->Show (); p->Show (3.4 , 7 ); return 0 ; }
早绑定,晚绑定 虚函数表 final禁止继承 左值、右值、亡值、表达式 引用折叠
引用折叠只能应用于间接创建的引用的引用,如类型别名或模板参数
X& &、X& &&、和X&& &都折叠成类型X&
X&& && 折叠成类型X&&
std::move 显式的将一个左值转化为对应的右值引用类型。
1 2 int &&rr1 = 42 ;int &&rr3 = std::move (rr1);
定义
1 2 3 4 template <typename T>typename std::remove_reference<T>::type&& my_move (T &&t) { return static_cast <typename std::remove_reference<T>::type &&>(t); }
std::ref std::bind 为函数绑定参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #include <iostream> #include <functional> #include <vector> #include <algorithm> using namespace std::placeholders;bool check_size (const std::string &s, std::string::size_type sz) { return s.size () < sz; }int sum (int a,int b) { return a; }std::ostream &print (std::ostream &os,const std::string&str,char c) { return os << str << c << ' ' ; } void shorter_words (std::vector<std::string> &vec_str) { auto it = std::stable_partition (vec_str.begin (), vec_str.end (), std::bind (check_size,_1,6 )); std::for_each(vec_str.cbegin (), vec_str.cend (), [](const std::string &str) { std::cout << str << ' ' ; }); puts ("" ); auto size = it - vec_str.cbegin (); std::cout <<"words less than 6 count is:" << size; }int main (int argc, char const *argv[]) { auto check_6 = std::bind (check_size, _1, 6 ); std::string a = "hello12" ; std::cout << check_6 (a) << std::endl; auto u_sum = std::bind (sum, _2, _1); std::cout << u_sum (1 , 2 )<<std::endl; std::vector<std::string> words = {"haha" , "funcking" , "bind" , "is" ,"so" , "learning!" }; std::ostream os (std::cout.rdbuf()) ; char c = 'a' ; std::for_each(words.begin (), words.end (), [&os, c](const std::string &str) { os << str<< c<<' ' ; }); puts ("" ); auto print_bind = std::bind (print, ref (os), _1, c); std::for_each(words.begin (), words.end (), print_bind); puts ("" ); shorter_words (words); return 0 ; }
绑定类的成员函数必须传递类对象或者类对象指针
1 2 3 4 BindTestClass bindTestClass (33 , "zack" ) ;auto memberbind = bind (&BindTestClass::MemberFun, &bindTestClass, placeholders::_1, placeholders::_2);
模板编程 std::forward完美转发 通常情况下,使用forward传递定位为模板类型的右值引用的函数参数。通过返回类型上的引用折叠,forward可以保持给定实参的左值/右值属性。
1 2 3 4 5 template <typename Type>intermediary (Type &&arg){ finalFcn (std::forward<Type>(arg)); ... }
源码:
1 2 3 4 template <typename _Tp>_GLIBCXX_NODISCARD constexpr _Tp&&forward (typename std::remove_reference<_Tp>::type& __t ) noexcept { return static_cast <_Tp&&>(__t ); }
可变参数模板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 template <typename T>std::ostream &print (std::ostream &os, const T &t) { return os << t; }template <typename T, typename ... Args>std::ostream &print (std::ostream &os, const T &t, const Args&... rest) { os << t << " " ; return print (os, rest...); }int main (int argc, char const *argv[]) { print (std::cout, "hello" , 42 ," good " ); return 0 ; }
sizeof…运算符
1 2 cout<< sizeof ...(args)<<endl;
折叠表达式优化C++17
1 2 3 4 5 6 template <typename ... Args>std::ostream& print2 (std::ostream &os, const Args&... args) { ((os << args << " " ), ...); return os; }
模板特化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #include <iostream> #include <string> #include <string.h> template <typename T1,typename T2>int compare (const T1 &v1, const T2& v2) { if (v1<v2) return -1 ; if (v2<v2) return 1 ; return 0 ; }template <>int compare (const std::string &v1, const std::string &v2) { std::cout << "特化版本" << std::endl; return strcmp (v1. c_str (), v2. c_str ()); }template <class T1 , class T2 >class Data {public : Data () { cout << "Data<T1, T2>" << endl; }private : T1 _d1; T2 _d2; };template <>class Data <int , char > {public : Data () { std::cout << "Data<int, char>" << std::endl; }private : int _d1; char _d2; };void TestVector () { Data<int , int > d1; Data<int , char > d2; }int main (int argc, char const *argv[]) { std::cout << compare (std::string ("he" ), std::string ("he" )); return 0 ; }
异常类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <iostream> #include <exception> class myExpection : public std::exception {public : const char * what () const throw () { return "C++ expection" ; } };class TimerExpection : public std::exception{public : TimerExpection () = default ; TimerExpection (unsigned code, const std::string &msg) : m_code (code), m_errorMsg (msg){} const char * what () const noexcept override { const std::string msg = "error code : " ; return msg.c_str (); }private : unsigned m_code; std::string m_errorMsg; };int main (int argc, char const *argv[]) { try { throw myExpection (); }catch (myExpection &e){ std::cout << e.what () << std::endl; } return 0 ; }
正则表达式 随机数 C++机制 前置声明 前向声明的适用情况
指向未定义类型的指针或引用 : 如果一个类仅使用另一个类的指针或引用,并不需要访问该类的成员,可以使用前向声明。这可以减少不必要的头文件包含。
1 2 3 4 5 6 7 8 9 class Session ; class LogicNode {public : std::shared_ptr<Session> getSession () const ; private : std::shared_ptr<Session> p_session; };
避免循环依赖 : 当两个类互相包含对方的实例时,前向声明可以打破这种循环依赖。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 cpp复制代码#ifndef A_H #define A_H class B ; class A {public : void doSomethingWithB (B* b) ;private : B* bInstance; };#endif #ifndef B_H #define B_H #include "A.h" class B {public : void doSomethingWithA (A* a) ;private : A* aInstance; };#endif
前向声明的限制
不能定义成员变量
不能调用成员函数
RAII机制 RAII是Resource Acquisition Is Initialization的缩写,即“资源获取即初始化”。它是C++语言的一种管理资源、避免资源泄漏的惯用法,利用栈的特点来实现,这一概念最早由Bjarne Stroustrup提出。在函数中由栈管理的临时对象,在函数结束时会自动析构,从而自动释放资源,因此,我们可以通过构造函数获取资源,通过析构函数释放资源。即:
1 2 3 4 5 6 7 Object () { } ~Object () { }
RVO、NRVO编译器优化 GCC 中使用 -fno-elide-constructors可以禁用RVO、NRVO优化
RVO(未命名的局部变量返回值优化)/NRVO(命名的局部变量返回值优化) : 编译器会尝试应用返回值优化。如果成功,局部变量将直接在调用者的存储空间中构造,不会调用拷贝或移动构造函数。
如果禁止了RVO/NRVO优化,返回局部变量应该优先使用移动构造函数,如果没有移动构造函数就使用拷贝构造函数。如果开启RVO/NRVO优化就不使用任何构造函数。
闭包 C++反射 反射机制允许程序在运行时借助API取得任何类的内部信息,并能直接操作对象的内部属性和方法。
C++不支持反射,PRC,WEB MVC,对象序列化 都依赖反射。
1、类对象反射
2、类成员数据反射
3、类成员函数反射
CRTP机制 奇异递归模板方法,通过继承实例为自身的模板方法
std::chrono 其定于都是在std::chrono这个命名空间下的
1、Time Points 时间点 表示一个特定的时间点,类模板,需要传递采用的时钟信息。
1 2 const std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now ();
time_since_epoch() 返回当前时间到时间纪元开始的durations
1 std::chrono::duration_cast <std::chrono::hours>(p1. time_since_epoch ()).count ()
2、Durations 时间长度 表示时间的长度。
1 秒 = 1000 毫秒 = 1000 * 1000 微秒 = 10001000 1000 纳秒 = 10001000 1000*1000
类型
定义
std::chrono::nanoseconds
std::chrono::duration<*/* int64 */*, std::nano >
std::chrono::microseconds
std::chrono::duration<*/* int55 */*, std::micro >
std::chrono::milliseconds
std::chrono::duration<*/* int45 */*, std::milli >
std::chrono::seconds
std::chrono::duration<*/* int35 */*>
std::chrono::minutes
std::chrono::duration<*/* int29 */*, std::ratio <60>>
std::chrono::hours
std::chrono::duration<*/* int23 */*, std::ratio <3600>>
std::chrono::days (C++20 起)
std::chrono::duration<*/* int25 */*, std::ratio <86400>>
std::chrono::weeks (C++20 起)
std::chrono::duration<*/* int22 */*, std::ratio <604800>>
std::chrono::months (C++20 起)
std::chrono::duration<*/* int20 */*, std::ratio <2629746>>
std::chrono::years (C++20 起)
std::chrono::duration<*/* int17 */*, std::ratio <31556952>>
std::chrono::duration_cast<> 转化时间的单位;
1 std::chrono::duration_cast <std::chrono::microseconds>(std::chrono::system_clock::now ().time_since_epoch ()).count ()
count() 返回时长计数次数的有符号算数类型;
3、Clocks 时钟 获取当前的时间点。
system_clock 表示系统的实际时间,不会受到系统时间调整的影响
steady_clock 稳定的时钟,时间不调整
high_resolution_clock 提供最小可表示的时间间隔
成员函数
now() 返回表示时间中的当前时间点Time_Point
应用实例: 定时器类 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class Timer {public : Timer () = default ; void start () { if (m_timeRuning){ throw TimerExpection (1 , "计时器被重复启动" ); } m_timeRuning = true ; m_begin = std::chrono::system_clock::now (); } void stop () { if (!m_timeRuning){ throw TimerExpection (2 , "计时器还没有启动" ); } m_timeRuning = false ; m_end = std::chrono::system_clock::now (); } double elapsed () { if (m_timeRuning){ return std::chrono::duration <double >(std::chrono::system_clock::now () - m_begin).count (); }else { if (m_begin == std::chrono::time_point <std::chrono::system_clock>()){ throw TimerExpection (3 , "计时器还未启动" ); }else { return std::chrono::duration <double >(m_end - m_begin).count (); } } }private : bool m_timeRuning = false ; std::chrono::time_point<std::chrono::system_clock> m_begin; std::chrono::time_point<std::chrono::system_clock> m_end; };int main () { Timer time; time.start (); std::this_thread::sleep_for (std::chrono::seconds (3 )); time.stop (); std::cout << time.elapsed () << std::endl; return 0 ; }
C风格时间日期 std::time_t 1 2 3 4 5 6 7 8 9 10 11 auto now = std::chrono::system_clock::now (); std::time_t now_time_t = std::chrono::system_clock::to_time_t (now); std::tm* now_tm = std::localtime (&now_time_t ); std::cout << "Current time: " << std::put_time (now_tm, "%Y-%m-%d %H:%M:%S" ) << std::endl;
外部库 Boost jsoncpp json格式解析 json中仅支持两种数据结构
对象(object) :以{开头
数组(array) :以[开头
pair键值对:
一个pair键值对的结构通常是: string(key) : value(value),其中key一般使用字符串 ,也可以使用数字,建议key只使用字符串。value取值 较为随意,可以是json支持的任意类型:object,array,string,number,true,false,null ,这说明了可以嵌套类型。
对象object:
object是多个pair的集合 ,以{开始,}作为结束,不同的pair之间用英文逗号来分割。object之间的pairt数据是无序的。
1 2 3 4 { "name" : "nick" , "age" : 18 }
数组array:
array是value的有序集合 ,以[开始,]作为结束,不同array元素之间也用英文逗号来分割。
1 2 3 4 5 6 7 8 9 10 11 [ { "name" : "nick" } , { "age" : 18 } , { "domin" : "cn" } ]
从文件中解析json: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 { "name" : "nick" , "age" : 18 }#include <iostream> #include <fstream> #include "jsoncpp.cpp" void parse_from_file () { std::ifstream ifs; ifs.open ("color.json" ); Json::Reader reader; Json::Value root; if (!reader.parse (ifs, root, false )){ std::cerr << "parse failed \n" ; return ; } std::string name = root["name" ].asString (); int age = root["age" ].asInt (); std::cout<<name <<" " <<age; ifs.close (); }int main (int argc, char const *argv[]) { parse_from_file (); return 0 ; }
protobuf grpc redis 启动redis
1 .\redis-server.exe .\redis.windows.conf
字符串String 设置一个键值对:
当键不存在时,设置键的值:
获取一个值:
Redis默认不支持中文显示,值作为中文将显示为二进制字符串,使用–raw可以显示原始数据
设置一个键的过期时间,单位秒:
设置一个带有过期时间的键值:
查看一个键的过期时间:
List列表 将元素添加到头部
从列表尾部添加元素
删除列表头部/尾部中的指定数量的元素
1 2 LPOP ListName 元素数 RPOP ListName 元素数
查看列表中的所有元素,-1代表列表中的最后一个元素位置
查看列表的长度
删除范围之外的元素
Set 不可重复的元素集合
添加元素,重复添加将返回false
查看集合中的内容
判断元素是否在集合中
删除元素
同时Reids支持集合之间的运算。
SortedSet 有序集合操作
Hash 记录键值对的集合
添加键值对
获取值
获取所有键值对
删除键值对
判断键是否存在
订阅者模式 消息队列Stream 杂谈: linux高性能服务器,单例模式,工厂模式,木朵网络库,一个方向深挖,opengl, opencv