zoukankan      html  css  js  c++  java
  • Mudo C++网络库第十一章学习笔记

    反思C++面向对象与虚函数

    • C++语言学习可以看《C++ Primer》这本书;
    • 在C++中进行面向对象编程会遇到其他语言中不存在的问题, 其本质原因是C++ class是值语义, 而非对象语义;

    朴实的C++设计

    • 实用当头, 朴实为贵, 好用才是王道;
    • C++ 是一门(最)复杂的编程语言, 语言虽复杂, 不代表一定要用复杂的方式来使用它;
    • 不一定非得有基类和派生类的设计才是好设计;
    • 一个类代表一个概念;
    • 让代码保持清晰, 给我们带来了显而易见的好处;
    • 不要因为某个技术流行而去用它, 除非它确实能降低程序的复杂度;
    • 在C++这样需要自己管理内存和对象生命期的语言里, 大规模使用面向对象、继承、多态多是自讨苦吃;

    C++编译器ABI的主要内容主要包括几个方面:

    • 函数传递的方式, 比如x86-64用寄存器来传函数的前4个整数参数;
    • 虚函数的回调方式, 通常是vptr/vtbl机制, 然后用vtbl[offset]来调用;
    • struct和class的内存布局, 通过偏移量来访问数据成员;
    • name mangling;
    • RTTI 和异常处理的实现;
    • 避免使用虚函数作为库的接口;
      • 因为这样会给保持二进制兼容性带来很大的麻烦;
    • JAVA的库都是.jar文件;

    虚函数作为库的接口的两大用途

    • 调用;
    • 回调, 也就是事件通知, 比如网络库的连接建立, 数据到达, 连接断开等;
    • 混合使用;

    iostream的用途与局限

    • C++ iostream的主要作用是让初学者有一个方便的命令行输入输出试验环境, 在真实的项目中很少用到iostream;
      • 不用花大量的精力在iostream的格式化与manipulator(格式操作符)上;
    • glibc定义的getline函数来读取不定长的行;
      • 返回的是malloc()分配的内存, 要求调用端自己free()掉;
    • iostream不是线程安全的;
    • 用到了多重继承和虚拟继承;
    • Google Protobuf是一种高效的网络传输格式, 它用一种协议描述语言来定义消息格式, 并且自动生成序列花代码;
    • C++的强大之处在于抽象不以性能损失为代价;
    • 数据抽象(data abstraction)是与面向面向对象(object-oriented)并列的一种编程范式(programming pragadigm);

    数据抽象所需的语言设施

    • 支持数据聚合(data aggregation);
    • 全局函数与重载;
    • 成员函数与private数据;
    • 拷贝控制(copy control);
    • C++ class是值语义, copy control是实现深拷贝的必要手段, 而且ADT用到的资源只涉及动态分配的内存, 所以深拷贝是可行的;
    • 操作符重载;
    • 效率无损, 抽象不代表低效;
      • 在C++中, 提高抽象的层次并不会降低效率;
    • 模板与泛型;
    • 数据抽象是C++的重要抽象手段, 适合封装数据, 它的语义简单, 容易使用;
    • 大多数class都是对象语义;

    C++经验谈

    • 练从难处练, 用从易处用;
    • 软件开发一定要时刻注意减少不必要的复杂度;
    • 作为应用程序的开发者, 对技术的运用要明智, 不要为了了解难度系数为10的问题而去强攻难度系数为100的问题, 这就本末倒置了;
    • 用异或来交换变量是错误的;
    • 未定义的行为, 在C/C++语言的一条语句中, 一个变量的值只允许改变一次(像x = x++这种代码都是未定义行为, 因为x有两次写入);
    • 现在的编译器会把std::reverse()这种简单函数自动内联展开, 生成出来的优化汇编代码和其他代码一样快;
    • 不要猜(guess), 要测(benchmark);
    • 不要重载全局::operator new();
      • 按现代C++的手法(RAII)来管理内存, 很难遇到什么内存方面的错误;
    • 内存管理的基本要求:
      • 内存管理的基本要求是不重不漏 -- 既不重复delete, 也不漏掉delete;
      • new/delete配对, 不仅是个数相等, 还隐含了new和delete的调用本身要匹配, 不要东家借的东西西家还;
      • 用系统默认的malloc()分配的内存要交给系统默认的free()去释放;
      • 用系统默认的new表达式创建的对象要交给系统默认的delete表达式去析构并释放;
      • 用系统默认的new[]表达式创建的对象要交给系统默认的delete[]表达式去析构并释放;
      • 用系统默认的::operator new()分配的内存要交给系统默认的::operator delete()去释放;
      • 用placement new创建的对象要用placement delete(为了表述方便, 姑且这么说吧)去析构(其实就是直接调用析构函数);
      • 从某个内存池A分配的内存要还给这个内存池;
      • 如果定制new/delete, 那么要按规矩来;
        • 检查代码中的内存错误;
        • 优化性能;
        • 获得内存使用的统计数据;
    • 脚本语言解释器代码:
      • Python的代码很好读;
    • C语言的static关键字的两种用法:
      • 用于函数内部修饰变量, 即函数内的静态变量; 使用静态变量的函数一般是不可重入的, 也不是线程安全的;
      • 用在文件级别(函数体之外), 修饰变量或函数, 表示该变量或函数只能在本文件可见, 其他文件看不到, 也访问不到该变量或函数(interal linkage);
    • C++语言的static关键子的四种用法:
      • static关键字又有了两种新用法: 用于修饰class的数据成员, 即所谓的静态成员, 这种数据成员的生存期大于class的对象(实体/instance);
      • 静态成员是每个class有一份, 普通数据成员是每个instance(实例)有一份, class variable(类变量)和instance variable(实例变量);
      • 用于修饰class的成员函数, 即所谓的静态成员函数, 静态成员函数只能访问class variable和其他静态程序函数, 不能访问instance variable或instance method;
    • 协议设计是网络编程的核心:
      • 消息格式: XML, JSON, Protobuf, 难的是消息内容;
    • 网络编程的三个层次:
      • 读过教程和文档, 做过练习 -- 读过《UNIX网络编程》《TCP/IP详解》并理解TCP/IP协议, 读过本系统的manpage;
      • 熟悉本系统TCP/IP协议栈的脾气;
        • 有可能出现TCP自连接(self-connection), 程序应该有所准备;
        • Linux内核会有bug, 比如某种TCP拥塞控制算法曾经出现TCP window clamping(窗口错位)bug, 导致吞吐量暴跌, 可以选用其他拥塞控制算法来绕开(work around)这个问题;
      • 自己写过一个简单的TCP/IP stack;
    • TCP网络编程有三个例子最值得学习研究: 分别是echo, chat, proxy都是长连接协议;
      • proxy的作用: 连接的管理更加复杂, 既要被动接受连接, 也要主动发起连接, 既要主动关闭连接, 也要被动关闭连接, 还要考虑两边速度不匹配;
    • 三本必看的书:
      • 谈到Unix编程和编程编程, W.Richard Stevens是个绕不开的人物;
        • [APUE]、两卷《UNIX网络编程》、三卷《TCP/IP详解》;
        • [UNPv2]其实跟网络编程关系不大, 是[APUE]在多线程和进程间通信(IPC)方面的补充;
        • 《TCP/IP详解》三卷, 用处不同, 应该区别对待;
      • 第一本《TCP/IP Illustrated, Vol. 1: The Protocols》(TCP/IP详解);
        • 从使用者(程序员)的角度, 以tcpdump为工具, 对TCP协议抽丝剥茧, 娓娓道来;
        • TCP作为一个可靠的传输层协议, 其核心有三点:
          • Positive acknowledgement with retransmission(对重传的积极响应) -- 可靠性;
          • Flow control using sliding window(包括Nagle算法等) -- 提高吞吐量;
          • Congestion(拥塞) control(包括slow start、congestion avoidance、fast retransmit) -- 防止过载造成丢包;
        • TCP像是一个自适应的节流阀, 根据管道的拥堵情况自动调整阀门的流量;
      • 第二本《Unix Network Programming, Vol.1:Networking API》统称UNP;
        • UNP是Sockets API的权威指南;
        • 网络编程远不是使用那十几个Sockets API那么简单, 一定要熟悉TCP/IP协议及其外在表现(比如打开和关闭Nagle算法对收发包延时的影响);
        • UNP中问版《UNIX网络编程》翻译得相当好, 译者杨继张先生是真懂网络编程的;
        • UNP很详细, 面面俱到, UDP、TCP、IPv4、IPv6都讲到了;
        • 讲得太详细, 重点不够突出;
          • 在具备基础之后, 学习任何新东西, 都要抓住主线, 突出重点, 对于关键理论的学习, 要集中精力, 速战速决;
          • 作者是先看的TCPv1, 花了大约两个月的时间, 然后再读UNP和APUE;
        • 第三本《Effective TCP/IP Programming》;
          • 这本书属于专家经验总结类的书籍;
        • 还值得一看的书:
          • 《TCP/IP Illustrated, Vol.2: The Implementation》, 称为TCPv2;
          • 工作中大可以把IP视为host-to-host的协议;
          • 《Pattern-Oriented Software Architecture Volume 2: Patterns for Concurrent and Networked Objects》, 简称POSA2;
            • 这本书总结了开发并发网络服务程序的模式, 是对UNP很好的补充;
            • POSA2强调模块化, 网络通信交给library/framework去做, 程序员写代码只关注业务逻辑(这是非常重要的思想);
            • 这本书对深入理解常用的event-driven网络库(libevent, Java Netty, Java Mina, Perl POE, Python Twisted)也很有帮助;
            • POSA2的代码是示意性的, 思想很好, 细节不佳;C++代码没有充分考虑资源的自动化管理(RAII);
    • 很多企业内部使用C++来构建自己的分布式系统基础架构, 并且有替换Java开源实现的趋势;
    • 学习C++只需要读一本大部头《The C++ Programming Language》或《C++ Primer》;
      • 《C++ Primer》的主要内容是精解C++语法(syntax)与语意(semantics)并介绍C++标准库的大部分内容(含STL);
    • C++的开源库: Google的Protobuf, leveldb, PCRE的C++封装, 还有就是作者的muduo库;
    • 如有时间可以读读Chromium中基础库源码, 在读Google开源的C++代码时要连注释一起细读;
    • 不建议一开始就读STL或Boost的源码, 因为编写通用的C++模板库和编写C++应用程序的知识体系相差很大;
    • 《Effective C++中文版》,《泛型编程与STL》, 《C++编程规范》;
    • 避免写出依赖于函数实参求值顺序的代码, C++操作读的优先级、结合性与表达式的求值顺序是无关的;
    • Google的C++编程规范和LLVM编程规范;
  • 相关阅读:
    一台服务器Nginx配置多个域名(一级域名或二级)
    Nginx启动和停止
    git删除本地及远程分支
    纯js实现复制内容到剪切板
    js 实现字符串转日期进行比较大小
    git 本地分支与远程分支 新建/删除/合并
    设置Git 记住密码
    炒鸡简单的javaScript的call和apply方法
    如何打war包
    Git 查看远程分支、本地分支、删除本地分支及远程分支
  • 原文地址:https://www.cnblogs.com/longjiang-uestc/p/9849444.html
Copyright © 2011-2022 走看看