原文链接:c++快速进阶-持续更新
类
构造
析构
继承
派生
多继承问题 当多继承类有共同基类时,基类会构造多次,为了消除基类的二义性,我们采用虚继承方式。
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 54 55 56 57 58 59 60 #include <iostream> class A {public: A(){ std ::cout <<"A create\n" ; } ~A(){ std ::cout <<"A delete\n" ; } }; class B : virtual public A{public: B():A(){ std ::cout <<"B create\n" ; } ~B(){ std ::cout <<"B delete\n" ; } }; class C : virtual public A{public: C():A(){ std ::cout <<"C create\n" ; } ~C(){ std ::cout <<"C delete\n" ; } }; class D : public B,C {public: D():B(),C(){ std ::cout <<"D create\n" ; } }; int main () { D d=D(); std ::cin .get(); return 0 ; }
虚函数: 基类的派生类是一个具有多个类特性的单类,在赋值时可以互相赋值。 类编译时是静态编译,若A-B-C依次派生,A指针赋值BC,调用的函数实际上还是A的函数。 实现编译器能识别,可以将需要的函数标记virtual,同时各派生类使用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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <iostream> class A {public : A (){ std::cout<<"A create\n" ; } void print () { std::cout<<"A print\n" ; } virtual void virtualprint () { std::cout<<"A virtualprint\n" ; } virtual void interface () =0 ; ~A (){ std::cout<<"A delete\n" ; } }; class B :virtual public A{public : B ():A (){ std::cout<<"B create\n" ; } void print () { std::cout<<"B print\n" ; } void virtualprint () override { std::cout<<"B virtualprint\n" ; } void interface () override { std::cout<<"B interface override\n" ; } ~B (){ std::cout<<"B delete\n" ; } }; int main () { B b=B (); A* a=&b; a->print (); a->virtualprint (); a->interface (); std::cin.get (); return 0 ; }
可见性: pri:仅类内 pro:仅派生类内 pub:全
友元: friend关键字,标记函数,类,类成员函数,在不破坏可见性条件下操作类内私有对象
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 #include <iostream> class B ;class A {friend void get_private_a (A const &a) ;friend class B ;private : int a; public : A (int a):a (a){ std::cout<<"A create:" <<a<<"\n" ; } ~A (){ std::cout<<"A delete\n" ; } }; class B :virtual public A{public : B (int a):A (a){ std::cout<<"B create:" <<a<<"\n" ; } void get_private_a (A const &a) { std::cout<<"A.a:" <<a.a<<"\n" ; } ~B (){ std::cout<<"B delete\n" ; } }; void get_private_a (A const &a) { std::cout<<"A.a:" <<a.a<<"\n" ; } int main () { A a=A (2 ); B b=B (0 ); get_private_a (a); b.get_private_a (a); std::cin.get (); return 0 ; }
常量特性
作用范围 const int* a/ int const* a: a不能修改(指针指向内容不能修改),可以重新赋值指针 int const a: a本身不能修改,*a可以修改
常量限制 const修饰成员函数表明这个函数不会修改类内成员变量。 const 对象只能调用const成员函数,这保证了限制的完备性,变量不会有任何变化。
多个函数 const可以区分同名函数,但不能区分构造函数
突破限制 标记mutable标记的变量可以被const修改
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 class A {private : int a; const int b; mutable int c; public : A (int a):a (a){ std::cout<<"A create:" <<a<<"\n" ; } void seta (int a) { this ->a=a; } void setc (int c) const { this ->c=c; } ~A (){ std::cout<<"A delete\n" ; } };
初始列表 在构造函数时使用构造列表实例化对象。在cpp的类中,classA a作为一个成员变量,在构造函数被赋值a=classA(0),如果存在classA()缺省构造,则A会构造2次,构造列表能避免
三元操作符 a= cond1?cond2:1:2 可嵌套
构造实例 构造空间 栈:classA a;==classA a=classA(); 堆:new delete malloc free
构造实例类型 explicit 作用于构造函数,不允许隐式类型转换
运算符重载 operator++/–/+/-/==/!= 运算符重载
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 54 55 56 57 #include <iostream> class Coordinate2D {private : int x; int y; public : Coordinate2D (int x,int y):x (x),y (y){ std::cout<<"Coordinate2D create:" <<x<<',' <<y<<"\n" ; } Coordinate2D operator +(const Coordinate2D& other){ return Coordinate2D (this ->x+other.x,this ->y+other.y); } Coordinate2D operator -(const Coordinate2D& other){ return Coordinate2D (this ->x-other.x,this ->y-other.y); } Coordinate2D& operator ++(){ x++; y++; return *this ; } Coordinate2D operator ++(int ){ Coordinate2D old=*this ; x++; y++; return old; } friend std::ostream& operator <<(std::ostream& os,const Coordinate2D& cod){ os<<"(" <<cod.x<<',' <<cod.y<<')' <<'\n' ; return os; } ~Coordinate2D (){ std::cout<<*this <<" delete\n" ; } }; int main () { Coordinate2D o=Coordinate2D (0 ,0 ); Coordinate2D a=Coordinate2D (1 ,0 ); Coordinate2D b=Coordinate2D (0 ,-1 ); Coordinate2D c=a+(++b); std::cout<<'c' <<c; std::cout<<'a' <<a; std::cin.get (); return 0 ; }
this this就是类的当前实例的指针,this是否可修改取决于当前函数是否const
智能指针 memory库的unique_ptr 特性:
该指针只能存在一个,不能同时存在两个指向同一个对象的指针
使用make unique 安全构造
使用move指令移动1 2 std::unique_ptr<Coordinate2D> p=std::make_unique <Coordinate2D>(1 ,2 ); std::unique_ptr<Coordinate2D> p2=std::move (p);
shared ptr:使用引用计数控制对象 weak ptr:shared的弱引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 { std::unique_ptr<Coordinate2D> p1=std::make_unique <Coordinate2D>(1 ,0 ); std::unique_ptr<Coordinate2D> p2=std::move (p1); } std::shared_ptr<Coordinate2D> p2; { std::shared_ptr<Coordinate2D> p1=std::make_unique <Coordinate2D>(2 ,0 ); p2=p1; } std::weak_ptr<Coordinate2D> p4; { std::shared_ptr<Coordinate2D> p3=std::make_unique <Coordinate2D>(3 ,0 ); p4=p3; }
copy构造 将一个函数=delete,表示不允许拷贝构造
使用等于复制对象时,c++实际上是调用了复制构造函数,默认的是shallow copy,当对象的成员是指针时,这会导致两个实例指向了同一个堆中的对象,在析构删除实例的时候,会删除堆两次。
实现复制构造很重要
在函数传参时,尽量使用const reference,速度快,保证只读性,如果直接是类参数,每次函数栈都要复制删除一次
1 2 3 4 Coordinate2D(const Coordinate2D& cpy ) =delete;Coordinate2D(const Coordinate2D& cpy ) { Coordinate2D(cpy .x ,cpy .y ) ; }
stl https://zhuanlan.zhihu.com/p/564057584
vector 动态数组,连续对象优于连续指针,保证内存访问局部性
使用push问题:
push入对象每次都要从当前域将对象复制到vector空间中
每次vector变化都要重新复制所有数据 这会导致指数式变化
解决:
使用emplace_back替代push,直接让vector创建对象
reverse指定初始数组大小
1 2 3 4 5 6 std::vector<Coordinate2D> vcod; vcod.reserve (3 ); vcod.emplace_back (0 ,0 ); vcod.emplace_back (1 ,0 ); vcod.emplace_back (2 ,0 );
array array<type,size>,相比于c语言数组,array底层是宏和模板实现的,无性能开销,且提升代码安全和维护性
std::array<Coordinate2D,3> codarr{Coordinate2D(0,0),Coordinate2D(0,0),Coordinate2D(0,0)};
list set map unordered_set,unordered_map
链接 详见linux开发中的gcc编译
多值传参
定义结构体返回
直接参数指针
返回数组,tuple,pair或其他的
template
函数使用template实现函数模板
类使用template实现成员变量模板化1 2 3 4 5 template <typename T>bool equal (const T& a1,const T& a2) { return a1==a2; } std::cout<<equal <int >(1 ,2 );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 template <int N,class type >class Arr {private : int size=N; type arr[N]; public : Arr (){ for (int i=0 ;i++;i<size) arr[i]=0 ; } Arr (type x){ for (int i=0 ;i<size;i++) arr[i]=x; } void print () { for (int i=0 ;i<size;i++){ std::cout<<i<<":" <<arr[i]<<'\n' ; } } }; Arr<10,float > a (1.1 ) ;
auto 两面性:auto能够兼容API返回类型变化,但要保证auto变量的抽象性不被破坏
主要在数组迭代器中缩略变量的类型如std::vectorstd::string
函数指针,lambda与回调函数 函数指针是实现函数作为变量的方法 void(*func)(params)=funca; func(actual params);
为了简化奇怪的函数定义,我们使用typedef typedef void(*func)(params) func f=funca;
例如有个函数是遍历vector中对象,并实现一个print操作,这个函数传入vector加一个函数指针,实现指定操作。当传入不同函数指针实现不同操作,从而达到实现回调函数的目的。
为了进一步提升效率,回调函数可能非常多以至于不需要每次都将函数定义出来,直接使用lambda函数实现
[capture](arg){body}
如果lambda复杂,需要使用本地参数,需要指定capture,如=表示直接拷贝所有值,&或直接指定变量,还可以标记mutable修改对象值
当然,由于使用了捕获,需要使用functional库 std::function<void(int)>& f 来支持这种函数指针
回调函数通过函数指针实现传输,可以通过这种方式实现多任务分布式系统下的异步执行。例如点击一个按钮1s后恢复,不需要一直等待只需要传入恢复函数,按钮等待完毕后直接调用恢复。
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 #include <iostream> #include <String> void callback (const std::string& msg) { std::cout<<"delay callback:" <<msg; }; typedef void (*func_ptr) (const std::string&) ;void callprocess (func_ptr& func) { std::cout<<"processing...:" ; func ("callprocess calling callback function\n" ); } int main () { void (*func)(const std::string&)=callback; func ("direct function pointer\n" ); func_ptr funcp=&callback; callprocess (func); func_ptr lambda_func=[](const std::string& msg){std::cout<<"labmda function:" <<msg;}; callprocess (lambda_func); std::cin.get (); return 0 ; }
命名空间 using namespace 看上去简单,实际上难降低代码阅读性,cpp标准库命名蛇形,我们自定义函数可以使用帕斯卡,实现快速辨别
using类似import,会出现函数重名的现象,通过namespace a{}构建命名空间
时间库 chrono时间库:c++标准时间库 包括time_point duration clock 主要3个类。
需要原因:
显示时间
存在规划
通信同步的需求
解决: 获取系统时间变量并转为时间戳,然后转为localtime获取各时间或者直接使用strftime。 对于时间间隔可以使用时间戳差值进行比较。 线程挂起定义一个duration变量然后使用当前线程的sleep_for函数
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 void time_tick () { using namespace std::chrono; std::chrono::system_clock::time_point t1=std::chrono::system_clock::now (); time_t timestamp=system_clock::to_time_t (t1); std::tm* t3=std::localtime (×tamp); char tbuf[80 ]; std::strftime (tbuf,sizeof (tbuf),"%Y-%m-%d-%H-%M-%S" ,t3); std::cout<<"strftime:" <<tbuf<<"\n" ; } int main () { using namespace std::chrono; std::chrono::system_clock::time_point t1=std::chrono::system_clock::now (); time_t timestamp=system_clock::to_time_t (t1); std::cout<<"timestamp:" <<timestamp<<"\n" ; std::tm* t3=std::localtime (×tamp); std::cout<<"t3:" <<t3<<"\n" ; char tbuf[80 ]; std::strftime (tbuf,sizeof (tbuf),"%Y-%m-%d-%H-%M-%S" ,t3); std::cout<<"strftime:" <<tbuf<<"\n" ; std::cout<<"d:" <<t3->tm_mday<<" m:" <<t3->tm_mon+1 <<" y:" <<t3->tm_year+1900 <<" day in year:" <<t3->tm_yday<<"\n" ; duration<int ,std::ratio<1 >> dur (2 ); std::this_thread::sleep_for (dur); time_tick (); std::cin.get (); return 0 ; }
chrono库实现时序计时 要适当挂起保证任务不一直占用 literal的chrono literal空间的sleep实现
io复用与io阻塞 IO复用主要是在linux下实现网络通信的连接管理。
阻塞 BIO阻塞型,当系统调用读写时,系统未准备好时阻塞 NIO非阻塞,立即返回,通过轮询处理
复用 场景:网络通信中,程序需要读写数据,但如果使用阻塞io会导致整个程序会被单个io阻塞,导致可能数据读写不能立即响应。例如服务器与所有客户端通信,每个客户端维护一个fd,我们需要一个方法能够去处理存在事件的fd。 解决:select Poll epoll 思想:使用位图bitmap实现,服务器进程初始化一个位图,这个位图对应所有io,我们将这个位图交给内核去处理,当有事件时,对应fd会置位,我们遍历所有fd然后去读取或处理被置位的io即可 select:当没有事件时内核会一直阻塞,直到有事件,且bitmap每次都需要置位 poll:不会阻塞 epoll:不会阻塞,且会讲有事件的fd前置,只需要遍历前面的fd即可
多线程 thread库
linux系统中gcc的thread库基于pthread实现,可以直接使用。并且linux系统库使用c实现的多进程线程与通信更安全可靠
win下使用mingw的gcc编译会无法使用thread库,需要如下操作:
下载https://gitcode.com/mirrors/meganz/mingw-std-threads/tree/master项目下的thead相关的.h文件
将其放置到gcc的lib库中c++部分,如C:\gcc\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++ 3.使用#include<mingw.thread.h> 如下文件: mingw.condition_variable.h mingw.future.h mingw.invoke.h mingw.latch.h mingw.mutex.h mingw.shared_mutex.h mingw.thread.h
除非在win下有应用需求否在不建议使用c++的线程库
算法 字符串 数组 线性表 树:深度遍历和层次遍历 图:广度遍历 区间,矩阵,字典树
双指针,滑动窗口,哈希表,二分查找 递归,回溯,分治,堆(优先队列) 动态规划
unordered_set,unordered_map min,max swap queue dequeue stack priority_queue:greater创建小堆 initializer_list decltype lower_bound fabsasm
项目 目前计划准备2两个:C++开发项目和一个unity项目
要了解的库有 json,linux内核库,muduo 要了解的工具 cmake,redis
多线程网络项目