C++ 学习记录

2018.10.11

  1. 学习了序列化,反序列化的概念
  2. 添加链接描述

2018.10.10

  1. 经过他群里的讨论,了解了一个 gcc 黑科技:struct { int len; char data[0]; }; 很奇特!在这里 data 不包含数据,不占用空间,只是一个数组名字而已,如果需要分配,则在struct 紧跟着的连续空间分配。
  2. 复习了 std::function , lambda , bind ,tuple
  3. 学习了群主博客中的组合函数(链式调用) Compose(f, g, h, i).run(args)

2018.10.9

  1. 学习了 lazy 延迟创建的模板类,和我之前学的代理模式作用有些类似,不过这个可以容纳所有返回类型,所有参数类型的对象,而且代码简洁(当然书写的简洁,代码还是可能膨胀)。
    启示:
    std::function 函数(回调)机制可以延迟实现!用在 io 中就是“异步”。
  2. 了解了 std::signals2 的用法,其涉及到 connection ,类似 Qt 中的信号槽机制,而且 connection 还可断开 disconnect,使用比较灵活
  3. 学习 std::hash 的使用2
  4. 学习了如何模仿 python 中的 range,也算是学习了模板的应用,
    (1)如何应对多个参数的功能?重载!
    (2)如何统一对象 ?不可能每种功能一种对象吧——提供默认参数。
    (3)同时学习了内部 iterator 的原理,对容器来说,需要重载 begin(), end();对迭代器来说,要访问就要重载 operator*,要自增自减就要重载 operator++,operator-- ,其中还涉及到 const 的使用, iterator 含外部容器类引用等细节。
    (4)如何使用自定义 range 容器?针对不同参数,返回不同的容器对象,向外提供 range 借口,在借口中统一参数类型。
    (5)not_eq 不可重载,其默认调用 !=
  5. 学习了群主对命令模式的改进,“解决了”命令爆炸的情况(不是真的解决,只是用模板极大地简化了书写)。经典模式就是 command.wrap(reciever, &function)。下午自己实现了下 command 的静态写法。遇到了两个问题:
    (1)auto it = holder.front(); holder.pop_front() 在容器中 pop 出去的内存是不存在了的,我猜想是容器内部实现的,在 pop_front() 之后再引用 it 就会导致程序崩溃。mark !
    (2)类型匹配时,出现 no matching funciton call 对应代码 template<typename Class, typename... Class_Args, class Obj, typename... Args> void wrap(result(Class::*fun)(Class_Args...), Obj&& obj, Args&&... args)
    我的错误就在 result(Obj::fun) ,导致 no matching function ,类函数的地址和对象函数的地址是不一样的概念,所以调用的时候 wrap( &classname,…) 而不应该是 &Obj…。
    我学到了什么?
    (1)加深了对命令模式的理解。设计模式并不一定局限于面对对象程序设计,也可以用模板实现(CRTP就是最好的例子)
    (2)学习了模板的书写
    (3)复习了对象内部成员指针的用法 ::
    , 学习了如何包装成员函数(以往我包装函数都是局限于 non-member_funtion
  6. 学习了群主对于 逗号表达式 的应用,可以在逗号前展开可变参数,也可以在逗号前 decltype 推断类型。具体参考 链接
  7. 顺带了解了链式调用的概念。。。。最近老碰到 C# ,这个语言优点还蛮多的。

2018.10.8

  1. 学习了 makefile 的基本内容,“参考书”——跟我一起写makefile
    日后这个网站,只当 C++ primer 一般使用,需要用时再查询
  2. 学习了 boost.lexical_cast 的实现
  3. 找到了群主的 github 江南
  4. 自己实现了简易 lexical_cast
  5. 学习了 optional 的简单使用,和模拟实现。典型的应用情景是函数调用时,如需根据条件返回一个对象(有效)或默认对象(无效),若该对象构造成本很高(资源分配等),可用optional返回一个空对象,提高效率。参考 std::optional
    注意:
    (1)就地创建:
    optional要求类型T具有拷贝语义,因为它内部会保存值的拷贝,但很多时候复杂对象的拷贝代价很高,而且这个值仅仅作为拷贝的临时用途,是一种浪费。因此optional库提供出了"就地创建"的概念,可以不要求类型具有拷贝语义,直接用构造函数所需的参数创建对象,这导致发展处了另一个Boost库–in_place_factory.
    (2)延迟创建,促进 std::lazy 的实现。判返回的数据是否有效
    (3)参考 如何使用 std::optional
    绝佳的理解 std::optional 的例子
std::optional<std::string> UI::FindUserNick()
{
    if (nick_available)
        return { mStrNickName };

    return std::nullopt; // same as return { };
}

// use:
std::optional<std::string> UserNick = UI->FindUserNick();
if (UserNick)
    Show(*UserNick);

启发:有时,含参数模板,返回值可以返回空的 return std::null_opt 一样的值,减少构造
6. 顺带在 C++ 参考上学习了 optional 和 advance, std::pair<T, bool>, next. prev, std::refrence_wrapper
7. 学习了 Lazy 延迟创建的技术

2018.10.7

  1. 今天学习了 boost::any 的源码,收获良多。
    (1)使用万能的中间层!将模板参数延迟到 any 内部的模板派生类,any 只需要持有一个基类指针即可做到类型擦除 !很棒!
    (2)boost::any 很注重异常安全,多使用 swap
    (3)读源码的过程中遇到了跨平台,跨编译器的一些说明,例如 BOOST_NO_RVALUEREFERENCE 等,参考博客 boost 源码中的一些说明
    (4)其实在测试的过程中也发现了 any 也不是万能的,因为只是存储上任意了,取出来时还是需要判断类型的,any_cast 就需要模板参数,而且 any_cast 比较复杂,我至今没看明白所有。
  2. 自己粗略实现了 any
    遇到不少问题:
    (1)is a inaccessable base of … 基类无法访问到派生类——很别扭,后来在 stackoverflow 发现问题处在 public 继承上。如果是 private 继承,外部无法看到 derived 的 base 部分。
    (2)因为编译器的warning, 学习了“关键方法”的定义,例如,虚表会定义在关键方法(第一个虚函数)被定义的源文件中,作为共享;但如果 inline 在 .h 文件,极其容易被扩散到 include 该 .h 的所有文件中, 会增加连接器的处理时间。
    参考博客:好文章
  3. 遇到了派生类虚函数覆盖基类同名不同签名的问题——采用 using 生命,有时还可 private
  4. 发现自己的 singleton 可变参数模板还是有缺陷的——如果一个对象有几种构造函数,单例模式只会调用其中一个,而且只有一次。我的目标是——针对每种构造函数,控制单例模式!代码已实现。
    遇到了一些问题:
    (1)刚开始想通过 boost::any 存储不同的构造方法,例如 std::functionstd::shared_ptr > 之类的,但是发现,不好取出来
    (2)借鉴超级对象池的实现,通过 typeid 手段,根据构造方法的名字,映射到某一单例,成功
    (3)还考虑到一个问题,我想到了对象池,回收单例,说不定可以提升性能 ?经过一番探索,我明白没有这个必要——首先,单例本身需求小,而这样的单例模式 + 对象池略显复杂,在实际中说不定还会损耗性能;第二点,只要在 map 中存有单例的引用,该单例就一直生存到程序结束,不需要考虑回收和名字同步问题。

2018.10.6

  1. 学习了超级对象池的原理。不过自己尚未实现自定义删除器无法删除,以及第二次使用回收过后的对象。
  2. 学习了万能函数包装器的概念,以及完美语义转发,左右值引用。&& 如果被左值初始化,会变成左值引用,
  3. 重新编译了 boost 库 sudo ./b2 install
  4. 在博客good的帮助下,成功编译了 boost.asio,命令是 g++ try.cpp -lboost_system -I/usr/local/include/boost -L/usr/local/lib -o try -lboost_thread -lpthread

2018.10.5

  1. 通过 CRTP 实现了静态织入的 AOP
  2. C++ 中的类型擦除概念——
    (1)继承机制,基类为派生类提供统一的借口,性能上有缺陷
    (2)模板机制,但有类型必须指定的限制
    (3)boost.variant 可容纳不同类型的对象,但必须是事先存在的类型
    (4)boost.any 可以容纳任何类型,但是取出时还是需要指定类型
    (5)闭包 lambda 表达式,std::function<> 可以代表一个操作,从而擦除对象类型
  3. 再次学习了 ScopeGuard,修正了转移构造函数的缺点,还加上了线程安全控制(std::mutex) ,加上了泛型的
    template < typanme funType> ,借鉴了 Loki 源码中的做法,把 ScopeGuard 构造函数设为private, 向外提供 makeGuard 借口。最后,完善了可变参数的 ScopeGuard 。大致如下:

    namespace YHL {

    template<typename funType>
    class scopeGuard final : boost::noncopyable {
    private:
        funType onExitScope;
        bool dismissed;
        std::mutex mtx;

        explicit scopeGuard(funType _onExitScope, funType acquire = []{})
            : onExitScope(_onExitScope), dismissed(false)
        { acquire(); }
    public:

        ~scopeGuard() noexcept {
            std::lock_guard<std::mutex> lck(mtx);

            if(dismissed == false) {
                dismissed = true;
                onExitScope();
            }
        }
        // 转移构造
        scopeGuard(scopeGuard&& other)
            : onExitScope(other.onExitScope),
                dismissed(other.dismissed) {
            other.Dismiss (true);
        }
        // 转移操作符
        scopeGuard& operator=(scopeGuard&& other) {
            if( this not_eq &other ) {
                this->onExitScope = other.onExitScope;
                this->dismissed = other.dismissed;
                other.dismissed = true;
            }
            return *this;
        }

        void Dismiss(const bool _dismissed) {
            std::lock_guard<std::mutex> lck(mtx);
            dismissed = _dismissed;
        }

        static scopeGuard<funType> makeGuard(funType _onExitScope, funType acquire = []{}) {
            return scopeGuard<funType>(_onExitScope, acquire);
        }
    };

    /*
    template <typename funType>
    inline scopeGuard0<funType> makeGuard(funType onExitScope, funType acquire = []{}) {
        return scopeGuard0<funType>::makeGuard (onExitScope, acquire);
    }*/

    template <typename funType, typename... Args>
    inline scopeGuard<std::function<void()> > makeGuard(funType&& onExitScope, Args&&... args) {
        return scopeGuard<std::function<void()> >::makeGuard([&]{
            onExitScope(std::forward<Args>(args)...);
        });
    }

}

缺点,书写麻烦,因为模板参数的原因,暂未实现宏定义

2018.10.4

  1. 把代码上传到 github
    (1)git add main.cpp
    (2)git status
    (3)git add ./
    (4)git status
    (5)git commit -m “try”
    (6)git remote add origin https://github.com/FluenceYHL/Try-How-to-git.git
    (7)git push -u origin branch
  2. 重写了一遍线程池,对 conditional_variable 以及 mutex 的认识加深。mutex 互斥量在同一时间内保证只有一把锁,其他线程无法获取这把锁。锁依靠 Acquire 和 Release 语义构成了临界区,保护了共享内容。例如,在线程池中,锁的作用是保护任务 task 队列(不能同时多个线程访问)和一些控制变量。
  3. 用面向对象的思想包装了 pthread ,实现了建议的 C++ std::mutex 和 std::lock_guard。
  4. 学习了对象池的实现。
  5. 学习了如何生成自己的 .a, .lib 静态库文件
  6. 学习了单例模式 + 可变参数
  7. 学习了加强版的 mapSingleton ,可以控制单例的数量
  8. 学习了注册版工厂模式
  9. 复习了 AOP 面向切面编程的概念,编码了 AOP 的简易实现
  10. 学习了 std::enable_shared_from_this 和 shared_from_this 的用法和易错点。不可以 return shared_ptr<this>, 只能 继承自 std::enable_shared_from_this ,自带 shared_from_this()。

2018.10.3

  1. 编译运行一个 C++ 程序的流程:
    (1)g++ -std=c++14 promise.cpp -lpthread -o promise
    (2)./promise
  2. Ubuntu 16.04 配置 boost
    (1)sudo apt-cache search boost
    (2)sudo apt-get install libboost-dev
    (3)g++ -std=c++14 untitled.cpp -o untitled -lphread -lboost_system
    (4)./untitled
  3. Ubuntu 16.04 boost.asio
    (1)参考 编译链接选项
    (2)boost.asio 编译选项
    (3)boost.asio 入门编译选项
  4. 学习了std::future, std::packaged_task, std::promise, std::async 的用法。
    (1)future 顾名思义,线程未来的共享状态
    (2)promise 包装了 future,可以储存一个值
    (3)packaged_tack 包装了 future,可以存储可调用对象
    (4)async 包装了以上三个和 std::thread,不需要再创建线程,一般最好用 std::async 创建线程。std::async先将异步操作用std::packaged_task包装起来,然后将异步操作的结果放到std::promise中,这个过程就是创造未来的过程;外面再通过future.get/wait来获取这个未来的结果。
  5. 了解了 lock_guard,unique_lock,mutex(四种)
  6. 再次学习了线程池:线程池的关键是——一个线程池 + 任务队列
    每个线程都不断轮询任务队列,如果有可以执行的任务,就把任务放到线程中。
  7. Ubuntu 16.04 配置 log4cpp 日志库
    (1)sourseforge 中下载 1.1.2版本 log4cpp 1.1.2
    (2)进入下载目录
    (3)./configure (最好./configure --with-pthreads)
    (4)make
    (5)make check
    (6)make install
    (7)sudo subl /etc/profile.d/log4cpp.sh 用 sublime 打开之后添加以下两行 LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib export LD_LIBRARY_PATH
    (8)chmod a+x /etc/profile.d/log4cpp.sh 增加文件的可执行权限
    参考 https://blog.csdn.net/mugua250/article/details/7831279
    补充:在 Qt creator 工程 pro 中加入以下内容
INCLUDEPATH += /usr/local/include

LIBS += /usr/local/lib/liblog4cpp.a
LIBS     += /usr/local/lib/liblog4cpp.so
#LIBS     += /usr/local/lib/liblog4cpp.la
LIBS     += /usr/local/lib/liblog4cpp.so.5
LIBS     += /usr/local/lib/liblog4cpp.so.5.0.6
  1. Ubuntu 16.04 安装配置 opencv 链接
    注意点:一定要是最新版的
    在 Qt creator pro 编译选项中要加入以下内容:

INCLUDEPATH += /usr/local/include \ /usr/local/include/opencv \ /usr/local/include/opencv2 LIBS += /usr/local/lib/libopencv_highgui.so \ /usr/local/lib/libopencv_core.so \ /usr/local/lib/libopencv_imgproc.so \ /usr/local/lib/libopencv_imgcodecs.so

  1. Ubuntu 16.04 Qtcreator Pro文件配置 boost
    INCLUDEPATH += /usr/include/boost
    INCLUDEPATH += /usr/local/include/boost
    LIBS += /usr/local/lib/libboost_timer.a
    LIBS += /usr/local/lib/libboost_system.a
    LIBS += /usr/local/lib/libboost_thread.a
    LIBS += /usr/local/lib/libboost_math_c99.a
    LIBS += /usr/local/lib/libboost_atomic.a
    LIBS += /usr/local/lib/libboost_chrono.a
    LIBS += /usr/local/lib/libboost_container.a
    LIBS += /usr/local/lib/libboost_context.a
    LIBS += /usr/local/lib/libboost_contract.a
    LIBS += /usr/local/lib/libboost_coroutine.a
    LIBS += /usr/local/lib/libboost_date_time.a
    LIBS += /usr/local/lib/libboost_exception.a
    LIBS += /usr/local/lib/libboost_filesystem.a
    LIBS += /usr/local/lib/libboost_graph.a
    LIBS += /usr/local/lib/libboost_iostreams.a
    LIBS += /usr/local/lib/libboost_locale.a
    LIBS += /usr/local/lib/libboost_log.a
    LIBS += /usr/local/lib/libboost_math_c99l.a
    LIBS += /usr/local/lib/libboost_math_tr1.a
    LIBS += /usr/local/lib/libboost_math_tr1f.a
    LIBS += /usr/local/lib/libboost_math_tr1l.a
    LIBS += /usr/local/lib/libboost_python27.a
    LIBS += /usr/local/lib/libboost_random.a
    LIBS += /usr/local/lib/libboost_regex.a
    LIBS += /usr/local/lib/libboost_serialization.a
    LIBS += /usr/local/lib/libboost_signals.a

2018.10.2

  1. 学习了线程池的来源和应用场景——考虑,如果服务器需要处理数目巨大的请求,如果采取“即时创建,即时销毁”的策略,那么服务器会处于不断创建和销毁的状态下,尤其是当服务本身所占时间短的情况下,创建和销毁的部分将会不可忽略;有时还需要考虑服务器瓶颈,线程池可以限制最多同时访问的线程数目。
    实现:
    (1)采用预创建的方法,在应用程序启动之后,将立即创建一定数量的线程(N1),放入空闲队列中。这些线程都是处于阻塞(Suspended)状态,不消耗CPU,但占用较小的内存空间。当任务到来后,缓冲池选择一个空闲线程,把任务传入此线程中运行。当N1个线程都在处理任务后,缓冲池自动创建一定数量的新线程,用于处理更多的任务。在任务执行完毕后线程也不退出,而是继续保持在池中等待下一次的任务。当系统比较空闲时,大部分线程都一直处于暂停状态,线程池自动销毁一部分线程,回收系统资源。
    缺点:需要考虑同步的开销;因为线程池需要占用一定的空间,如果线程本身创建和销毁所占时间很短的话,线程池就略显不足了。
    适用场景(1)线程创建和开销所展开销较大的情况下(2) 实时性要求高,线程池不需要即时创建。
  2. 初步了解了 std::promise, std::future 和 std::package_task, std::synic 的基本用法。
  3. 学习了”锁“的内部原理。参考 并发基础: 锁
    关键两点(1)锁构建了临界区,同一时间内只有一个线程可以进入临界区(2)锁 lock 的 Acquire 语义(插入的限制,类似内存屏障)保证了临界区以下的代码不会被优化至 lock 之前;unlock 的 Release 语义,保证了unlock 以上的临界区代码不会被延迟至unlock 之后执行。
  4. 顺带复习了内存屏障的概念——主要分为编译器内存屏障(没有实际代码)和 CPU 内存屏障(存在实际指令)
  5. 学习了 volatile 的三种特点(1). 易变性,在汇编层面反映出来,就是两条语句,下一条语句不会直接使用上一条语句对应的volatile变量的寄存器内容,而是重新从内存中读取(2)“不可优化”特性。volatile告诉编译器,不要对我这个变量进行各种激进的优化,甚至将变量直接消除,保证程序员写在代码中的指令,一定会被执行(3)顺序性,能够保证Volatile变量间的顺序性,编译器不会进行乱序优化;但和 非 volatile 变量之间可能被编译器交换顺序。
    了解了 Java 的 volatile 是加强的。
    了解了 Happen Before
    了解了 volatile 的起源——和外设之间的数据交换。
    参考 C++ volatile
  6. 学习了 spinlock 的机制和易错点。参考 深入理解 spinlock
  7. 学习了 atomic 的本质。参考atomic 原子变量
  8. 浏览了如何构建个人网站
  9. 学习了虚拟主机的概念
  10. 了解了 Debian
  11. 在 linux 下 -S 输出汇编,观察了简单程序的几个步骤
  12. 在类 linux 下对 C++ 线程的操作需要加上 -lpthread 编译链接选项。可执行文件不可以加 .exe(这个是 windows 下)
  13. 之前一直不懂 do{} while(0) 的意义,学习了
    (1)#define 宏定义中局部作用域
    (2)取代 {} 段,而且可以随时跳出 break

2018.8.15

  1. 拜读了文章 技术人员如何创业,值得反思,技术并不是创业的全部,
  2. 学习了内存对齐的原理:
    原因:
    (1) 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
    (2) 性能原因:经过内存对齐后,CPU的内存访问速度大大提升。
    CPU 并不是一个一个字节看内存的,而是1 , 2 , 4 , 8 , 16 个字节来看(作为一小块),以块作为读写单位。[ 0 , 4 ]中选 4 个字节 和 [ 1 , 7 ] 中间选 4 个字节,前者 CPU 只要访问一次内存,后者要访问两次,而且容易出现内存碎片。
    参考博客 :
    (1)内存对齐的原理
    (2)计算内存对齐
  3. 见识了返回对象的 static 变量中的大陷阱 !因为 static 变量只有一份,前者容易被后者覆盖。参考博客 static 陷阱
  4. 复习了 C++ 内存的分布和虚函数原理。
    解决了三个困惑
    (1)为什么虚函数一定要借助指针 ?比如Base o = Derived example,是不能实现多态的,一直卡在子类 Derived 的内存布局上,原因其实在 Base ,Base o = Derived example 隐式类型转换之后,得到的是 Derived 内存布局中的前段,所以只是一个 Base 对象而已,通过这个 Base对象调用的只是 Base 的函数。所以要区分开基类和子类的内存布局,子类虚表中存在替换;但基类虚表不存在替换,还是原来的基类虚表 。
    指针可以实现多态的原因:指针获取的地址 + 偏移量 。实际得到的地址是基类对象地址,调用的就是基类的函数;实际得到的是子类地址,调用的是子类的函数。这里的地址,实际上就是虚表地址。
    多个对象继承时,还存在类型转换和地址转移的过程。
    参考博客:图解 C++ 虚函数
    (2)同一个类,使用的是同一张虚表(因为类定义是一样的),和同样的 RTTI (type_info)机制。
    留下了几个问题:
    (1)Base o = Derived example 这里,Base 得到的是 Derived 内存的前段部分(属于 Base 定义的),那么虚表呢? Base 的虚表按理应该还是 Derived 才对,难道这里还有虚表地址转换吗?
    (2)没有虚函数的类有虚表吗?如果没有虚表,那类如何调用成员函数(this 指针访问静态存储区,总需要一张表的吧)
    (3)虚表储存在哪儿?静态存储区?还是堆区?还是栈区?或者寄存器?我的猜想:静态存储区。
    (4)如果没有对象实体,虚表还存在吗?
  5. 学习了va_start,va_end,va_arg 的用法,可以得到参数列表

2018.8.14

  1. 不要 delete void* 指针(内置类型除外),原因是 void* 分配的内存是没有类型的,只是一块内存而已,不存在析构函数。如果 void *ptr = new exampledelete ptr ; ptr = nullptr ; 会忘记调用析构函数,容易造成内存泄漏。
    参考博客:不要 delete void *
    关于 void* 的介绍参考 void 指针
  2. 学习了类型萃取技术(类似于 Java 中的反射)
    初步入门:浅显易懂
    中级入门:类型萃取的高级应用
    技巧 :模板做设计时,一般建议在模板定义内部,为模板的每个类型参数提供typedef定义,这样在泛型代码中可以很容易地访问或抽取这些类型

2018.8.11

  1. 再次了解了 RAII 的基本原理和使用,例如文件流和对付死锁时的简单应用。
  2. 初步了解了 RAII 工厂,其实原理就和原来我写的工厂模式中的垃圾收集器一样,只不过,工厂只负责单一职能,分层更好。
  3. 了解了 scopeGuard 的原理和基本使用

2018.8.2

  1. 初步了解了 RAII 的概念和在单例模式中的应用。
    参考博客 C++ RAII 的设计技巧RTII 的分析
    RAII ( Resource Acquistion Is Initialization ) ,C++ 语言中一种资源管理,避免资源泄露的方法。一个对象在其构造时获取资源,在生命周期内控制对资源的有效访问,最后析构的时候释放资源。
    RAII 的优点:不用显式释放资源,将资源用类封装起来,资源操作都封装在类的内部;可以做到异常安全,如果程序发生异常,造成提前退出,如果没释放资源就会造成资源泄露,而 RAII 就可以解决这个问题。
    本质: 将资源的获取和释放与对象的构造和析构对应起来。
    在单例模式中的应用:内部类的自动清理,如下:
class Litter {
public:
	~Litter() {
		cout << "\n清理单例类" << endl ;
		if ( null not_eq nullptr )
			delete null ;
		null = nullptr ;
	}
} ;

// GC 清理类
static Litter litter ;
  1. 复习了 final 在防止继承的用法 class Entity final {} ;
    虚拟继承在单例模式 Lock<Entity> 中的作用:派生类会尝试直接调用 Lock 的构造函数;如果不是虚拟继承,派生类会调用 Entity 的构造函数,Entity 会调用 Lock 的构造函数。

2018.8.1

  1. 接触了 std::thread 的基本用法,构造,含参数构造,获取 CPU 核心,sleep_forstd::chronostd::this_thread 的使用
  2. joindetach 。在使用前最好判断 if joinable ,因为一个线程只能 join 或 detach 一次。
    区别:
    join 会阻塞主线程,知道当前线程执行完毕,再 join 会合,再清理线程资源
    detach 线程会和线程对象分离,对象可以销毁,但是线程依旧在运行
  3. std::mutex 加锁的使用
  4. 原子变量 atomic_int 表示最原子的操作,不允许任何操作打断直到原子操作结束,效率比加锁高。
    自定义实现 atomic_t 原子变量(失败)
  5. 继承自 thread 的类可以拓展功能,例如 system( str ) ,
  6. 封装 thread 实现 RAII,可以实现异常安全,及时释放资源(失败)
  7. 线程可以交换,不可以赋值
  8. 线程可以移动,但是原来的会销毁

2018.5.18

  1. 今天,才明白——要重载,operator newoperator delete 真是大神能做的。一个项目,我修改了文件的包含关系,程序编译成功了,但是程序一运行就崩溃了!经过多次调试输出,才发现,程序崩溃在 main 函数之前——所以,问题出在 main 函数之前,经过一番学习,猜测是静态变量和全局变量的初始化,用到了…new,然后我把这些 operator newoperator delete 注释之后,程序正常 !
    修改文件之前,没有崩溃,应该和文件的包含关系有关,谁在前,谁在后——也说明了一点,静态变量之间最好不要有牵连!我这次项目,牵连就出在——内存检测的容器,初始化不一定在 operator newoperator delete 之前,所以,容器可能还不存在!
    学习了!
    static constexpr char* password = "Fluence_YHL" ;
    问题是:warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings] static constexpr char* password = "Fluence_YHL" ; 改为:static constexpr char* password = (char*)“Fluence_YHL” ;` 就行了

2018.5.15

  1. _alloca 函数,C 的库函数,允许在栈区动态分配空间,而且申请空间之后会自动释放——栈区也可以动态分配
    不过 _alloca 函数是不安全的。
  2. _msize () 函数,C 库函数,可以得出 malloc 为指针分配了多少空间。

2018.5.14

  1. 今天遇到了 constexpr 关键字,C++11 新特性
    极好的参考博客 Effective Modern C++ 条款15 尽可能使用constexpr
    const 的对比学习:
    (1). constexpr 修饰的对象一定是 const 的,但是 const 修饰的对象不一定是 constexpr 的。
    (2). constexpr 修饰的变量在编译时就有确定的值,编译器会确保这一点,但是 const 修饰的变量在编译期不一定要用已知的值初始化。
    例子:
    int a = 10 ;
    const int b = a ;
    std::array<int , b > // 错误
    如果想要编译器保证变量编译期有值,即上下文请求了一个编译期间的常量,那么能用的工具是constexpr,而不是const
    (3). constexpr函数在传入编译期已知值作为参数时,会在编译期间生成结果;如果编译期得不出结果, constexpr 修饰的函数就和普通的函数是一样的。
    C++ 14 允许 constexpr 函数内部有局部变量,和 ifswitch 条件选择语句。

1. 2018.5.12

  1. 尝试菱形继承,发现根类会构造,析构两次
    学习了虚继承 virtual public [基类 classname]
    极好的参考博客:从内存布局看C++虚继承的实现原理
  2. 学习了一个技巧——g++ 命令查看虚表内容( 例 empty(19).cpp )
    g++ -std=c++11 -fdump-class-hierarchy empty(19).cpp
    get 了神技能,可以观察到 vTable 的分布和对象的布局
  3. 学习了 typeid 关键字的使用和表面原理
    typeid 在对象是否具有虚函数时是动态判断的
    typeid 在判断指针和指针的解引用时的区别
    参考博客 typeid
    进一步学习:
    通过观察 vTable 布局,可以发现,主虚表和次虚表的前面都含有一个一样的 RTTI 的类型信息,子类和基类的不一样。正是利用这一点,运行时, typeid 通过 vptr 找到虚函数表,虚函数表前面的那个位置存储了类型信息对象,编译器会为每一个不同的类生成唯一标识该类型的类型信息对象,typeid 就是返回这个对象的引用。
    dynamic_cast 的动态原理和 typeid 类似。
    但是,dynamic_cast 会略显复杂,在继承关系中,RTTI 对象还会保存其父类的RTTI 对象的指针,呈树状结构,有了继承关系,再递归从虚表中查找基类子对象在派生类中的偏移值,便可以确定最终返回地址。
  4. 今天通过菱形的虚继承,引出来了很多关于 C++ 内存布局的问题,尤其是虚表结构,拜读了某位博主的一系列博客
    C/C++杂记:虚函数的实现的基本原理
    C/C++杂记:深入虚表结构
    C/C++杂记:运行时类型识别(RTTI)与动态类型转换原理
    C/C++杂记:深入理解数据成员指针、函数成员指针
    茅塞顿开:
    运行时多态(虚函数机制),基类指针获取的 vptr 虚表指针是确定的,至于运行时调用哪个函数,还是不确定的,按根据偏移量来寻找真实调用的函数。
    如果基类指针指向的是基类对象,指针获取的是基类对象的vptr , 指向的的虚表当然全部是基类的函数了,运行时只会找到基类的函数
    如果基类指针指向的是子类对象,指针获取的是子类对象的vptr , 指向的虚表就是子类的虚表,其中可能有 从基类继承 过来的虚函数,也可能有 重写基类 的虚函数,还可能有子类自己声明的虚函数( 子类独有的 非虚函数 的不在虚表中 ),程序运行时真实调用的函数还不清楚,因为还不清楚子类 vptr 中基类的虚函数是否会被子类重写。 vptr 只是一个类似索引 的东西。
    为什么虚函数机制不是编译时多态?
    因为编译时,编译器只知道基类指针是基类类型的指针,但不知道基类指针指向的到底是基类对象还是子类对象,只有到运行时,找到真正的对象,看实际内存中最靠前 的虚表指针 vptr ,这时候已经没有了基类和子类的概念,因为机器只知道按照 vptr偏移量去寻找要调用的函数,找到谁就执行谁。
    我想这也就是为什么虚表指针 vptr 要放在对象的最前面。
  5. 多继承,或者是菱形等复杂继承时,会出现主虚表次虚表 的分类,子类和其中一个基类共享一个vptr ,节约空间。
    C/C++杂记:虚函数的实现的基本原理
  6. 遇到了 cin.ignore () 函数,顺便初步了解了一下cin.get () , cin.sync () , cin.clear ()
  7. 初步了解了策略模式
    参考博客:《JAVA与模式》之策略模式
    我还是不能理解,为什么策略模式可以解决 if else 过多和 switch 的问题。大致了解了一下,策略模式就像是一台机器,具备了针对同一种行为的多种策略——比如我要实现一个老师,学生,管理员不同登录方式的系统,就可以采用策略模式。
    初步实现了上述的登录 demo
  8. 留下的问题:
    typeidRTTI ,以及 vptr 的关系还不是很明确
    dynamic_castRTTI 的关系
    try 异常捕获和 RTTI 的关系
    内存对齐是什么
    尝试自己实现一个智能指针 smart_ptr<T> 在赋值操作上出问题

2018.5.9

  1. imlk 的指点下,大致明白了策略模式的优势所在。
    如何避免了 if else 或者switch
    for ( auto &it : user ) it.call_strategy () ;
    当有多个对象,进行同一个操作,具体操作不同时,就可以忽略判断,直接调用策略!
    如果就一个对象调用策略,就体现不出优势。
  2. 在测试策略模式的多对象同时操作时,我掉进了一个浅拷贝 的坑。再次体会到了 vector 的不安全和 深拷贝复制函数 的重要性。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值