美文网首页C++复习
读《google C++风格指南》有感

读《google C++风格指南》有感

作者: 凉拌姨妈好吃 | 来源:发表于2018-10-29 13:03 被阅读2次

    1. 复习内容

    1.1 匿名命名空间

    字面意思:声明命名空间时忽略名字
    编译器内部会为这个命名空间生成一个唯一的名字和using指令

    namespce {
        char c;
        int i;
        double d;
     }
    

    所以上面的代码在编译器内部类似于:

     namespace __UNIQUE_NAME_ {
         char c;
         int i;
         double d;
         }
     using namespace __UNIQUE_NAME_;
    

    它和静态变量的相似之处?
    匿名命名空间也具有内连接属性,也就是说名称的作用域被限制在当前文件中,无法在其他文件使用extern来扩展作用域
    它和静态变量相比更优在哪?
    对于多个同一文件的标识符函数只需要用一个匿名空间来声明,不需要多次输入static

    1.2 如何引用命名空间
    // 方式一
    ace::Mutex mutex;
    // 方式二
    using ace::Mutex;
    // 方式三
    using namespace ace;
    
    • 方式一
      只是在必要的时候运用域运算符::来引用指定空间里的标识符。
      适用于:当前编译单元内引用ace内的标识符不多且使用次数不多
    • 方式二
      只引入ace::Mutex一个标识符
      适用于:当前编译单元内ace::Mutex使用次数较多的情况
    • 方式三
      把ace里的全部标识符都引入当前命名空间中,此后ace内所有的标识符对于当前空间都是可见的
      适用于:当前编译单元内使用ace内的标识符较多,且不会出现标识符冲突的问题

    对于上面三种方式的选择应由一到三,因为越往后产生命名冲突的可能越大

    1.3 override与final
    • override:指定一个虚函数覆写另一个虚函数
      在成员声明或定义中,override确保该函数为虚并且复写来自基类的虚函数,若不是就会出现编译错误
    • final:指定派生类不能覆写虚函数,或类不能被继承
      在虚函数声明或定义中,final确保函数为虚且不可被派生类复写
      在类定义中,final指定此类不能被派生
    1.4 访问者模式
    • 定义:封装某些作用于某数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作
    • 自己的理解:在衣服店定制了几套衣服,成衣后我又想对某件衣服进行改变,但是已经不能改变衣服了,所以收到衣服后,我只能针对我想改变的那件,新增加一些小搭配(丝巾等等)
    • 含有角色:
      抽象访问者 访问者 抽象元素类 元素类 结构对象
    package yanbober.github.io;
    
    import java.util.ArrayList;
    import java.util.List;
    
    //Vistor(抽象访问者)
    interface Vistor {
        void visit(ConcreteElementNodeA node);
        void visit(ConcreteElementNodeB node);
    }
    //ConcreteVisitor(具体访问者)
    class ConcreteVisitorA implements Vistor {
        @Override
        public void visit(ConcreteElementNodeA node) {
            System.out.println(node.operationA());
        }
    
        @Override
        public void visit(ConcreteElementNodeB node) {
            System.out.println(node.operationB());
        }
    }
    
    class ConcreteVisitorB implements Vistor {
        @Override
        public void visit(ConcreteElementNodeA node) {
            System.out.println(node.operationA());
        }
    
        @Override
        public void visit(ConcreteElementNodeB node) {
            System.out.println(node.operationB());
        }
    }
    //Element(抽象元素)
    abstract class ElementNode {
        public abstract void accept(Vistor vistor);
    }
    //ConcreteElement(具体元素)
    class ConcreteElementNodeA extends ElementNode {
        @Override
        public void accept(Vistor vistor) {
            vistor.visit(this);
        }
    
        public String operationA() {
            return "ConcreteElementNodeA";
        }
    }
    
    class ConcreteElementNodeB extends ElementNode {
        @Override
        public void accept(Vistor vistor) {
            vistor.visit(this);
        }
    
        public String operationB() {
            return "ConcreteElementNodeB";
        }
    }
    //ObjectStructure(对象结构)
    class ObjectStructure {
        private List<ElementNode> nodeList = new ArrayList<>();
    
        public void action(Vistor vistor) {
            for (ElementNode node : nodeList) {
                node.accept(vistor);
            }
        }
    
        public void add(ElementNode node) {
            nodeList.add(node);
        }
    }
    //客户端
    public class Main {
        public static void main(String[] args) {
            ObjectStructure objectStructure = new ObjectStructure();
            objectStructure.add(new ConcreteElementNodeA());
            objectStructure.add(new ConcreteElementNodeB());
    
            Vistor vistor = new ConcreteVisitorA();
            objectStructure.action(vistor);
        }
    }
    

    参考:
    设计模式(行为型)之访问者模式(Visitor Pattern)
    23种设计模式(9):访问者模式

    1.5 何时捕获异常

    我们只能捕获我们能够处理的异常,能够恢复的异常将它捕获后恢复,像不能恢复的如,越界等就不要捕获了。
    对使用 C++ 异常处理应具有怎样的态度?

    1.6 可重入函数
    1.7 杂项
    • explicit构造函数:指定构造函数或转换函数为显式,即它不能用于隐式转换和复制初始化
    • 静态存储周期:static用于声明对象拥有静态存储期
    • POD(Plain Old Data):该类型包括标量类型,c/c++的基本类型,用户自定义的类类型(这里的类必须是无析构函数和构造函数/拷贝构造函数的类,其实也就相当于struct)
    • const_cast:用来移除变量的cv限定符
    • volatile:一般编译器在访问变量时,都会对变量进行优化。也就是不会每一次都去变量的内存中读数据,编译器会把变量值存在「寄存器」中,在下一次查询时直接到此取数据,这样访问速度就大大增加。
      而volatile修饰的变量,编译器每一次查询都会去内存中读取数据。它常用于多线程编程,当一个线程改变变量的值的时候,「寄存器」的值可能还未更新,所以必须从内存中读数据读到的才是真实的数据。
    • mutable:常用于不影响类的外部可变状态的成员,该成员在const函数内依旧可被修改
      下面是它在lambda表达式中的使用:
      虽然看起来修改了x,其实只是修改了x的拷贝
    int x{0};
    auto f1 = [=]() mutable {x = 42;};  // okay, 创建了一个函数类型的实例
    auto f2 = [=]()         {x = 42;};  // error, 不允许修改按值捕获的外部变量的值
    
    • std::function:通用多态的函数封装器,它的实例可以存储,复制及调用任何可调用目标
    // 存储自由函数
    std::function<void(int)> f_display = print_num;
    f_display(-9);
     
    // 存储 lambda
    std::function<void()> f_display_42 = [](){ print_num(42); };
    f_display_42();
     
    

    2. 代码风格的思考(仅针对客户端)

    2.1 在别人代码上进行迭代
    • 注意不要破坏别人原有的风格,对函数命名/变量命名应与前人统一
    • 修改之前应该仔细看看代码的结构,不要把本来耦合度很低的代码改成了耦合度极高的代码
    2.2 自己写代码
    • 在做csd时就思考好节点的构造
    • 低耦合
    • 代码简洁,清楚
    • 命名易于理解,别人在看你代码时,可以通过命名直接明白函数需要做什么
    • 考虑内存!考虑内存!需要做缓存的就做缓存

    下面以这个页面来对代码做一个梳理:
    首先有一个头部主要是放活动图片/标题等,中部是一个横向scroll,上面的btn可以刷新下面的纵向scroll里的节点信息


    • 首先拿到页面所需要信息(initData)
    • 根据信息从上往下在代码中对页面进行展示(initView)
    • 思考scroll是否需要做缓存,在这里我只对纵向scroll做了缓存
    • 初始化横向scroll,注册回调事件来刷新纵向scroll(initMiddleScroll,updateMiddleScroll)
    • 定义两个table:tableItemsSel 和 tableItemsSpare(用于存储缓存数据)
    • 封装纵向scroll的信息,使用一个table封装,这里的point用于等会插入缓存table(initScrollData)
    local tableItemsSel = {
      { data = data,
        point = nil,
        pos = {x = 0,y = 0}
    }
    }
    
    • 初始化纵向scroll,计算节点位置,更新tableItemsSel.pos(initScroll)
    • 填充纵向scroll

    设定一个值为预加载范围,如果在范围内就让节点可见,不在范围内就让节点不可见且插入缓存tableItemsSpare

    local scrollH = scroll的高度
    local tableShowH = {beginY = math.max(0,scroll的y轴偏移-0.5*scrollH),endY = scroll的y轴偏移+1.5*scrollH,}
    

    白色区域为我们的初始可视区域,红色区域就是我们现在扩大了的可视区域,超过这个可视区域,就让节点不可见且插入缓存tableItemsSpare


    if v.pos.y < tableShowH.beginY or v.pos.y > tableShowH.endY then
                    v.point:setVisible(false)
                    table.insert(self.tableItemsSpare,v.point)
                    v.point = nil
    end
    

    如果缓存数组里有节点就取出设为可见,再初始化它。否则新建节点


    • 设置scroll回调事件去重新填充scroll

    相关文章

      网友评论

        本文标题:读《google C++风格指南》有感

        本文链接:https://www.haomeiwen.com/subject/pcbtzftx.html