[已注销]对《C++编程思想(第1卷)》的笔记(9)

C++编程思想(第1卷)
  • 书名: C++编程思想(第1卷)
  • 作者: [美] Bruce Eckel
  • 副标题: 标准C++导引
  • 页数: 479
  • 出版社: 机械工业出版社
  • 出版年: 2002-9
  • 第114页
    C的缺陷:include头文件来声明函数,名字冲突……而且每次调用的时候都要传一个结构体指针
    作者又接着调侃C的弱类型:
    在C中,我们常常发现能使程序通过编译,然后我们必须再花力气使它工作。
    2011-12-16 15:18:49 回应
  • 第142页
    c++ class的访问保护检查是在编译阶段完成的,访问控制信息(哪些成员是private,哪些是public)不会传递给连接器,程序在运行期间也不会做检查。
    2012-01-12 20:30:04 回应
  • 第144页
    这里作者介绍了一种减少重复编译的技术:句柄类。
    不适用句柄类:
    接口代码
    // A.h 
    class A {
        struct  B {
            int i;
        } *b; // 嵌套定义的私有成员b是一个指向B的指针
    public:
        void method1();
        void method1();
    }
    
    实现代码
    // A.cpp
    void A::method1() {
        //do something
    }
    
    void A::method2() {
        //do something
    }
    
    客户端代码
    #include "A.h"
    int main() {
        A a;
        a.method1();
        a.method2();
    }
    
    因为在公共接口中定义了B,导致每一次对B进行修改以后,客户端代码都要进行重新编译(B的定义改变后,客户端中A重新定义以后的对象大小必然也改变,因为在A中嵌套定义了B的结构,因此A的结构随B而变)。解决方法是在接口部分只保留一个B类型的指针,把类的定义放到实现代码中。
    使用了句柄类:
    接口代码
    // A.h 
    class A {
        struct B; //仅仅声明
        B* b;      //因为A中的成员变量只是一个指针,大小由机器决定,因此在客户端中每次定义以后的大小都相同,无需重新编译
    public:
        void method1();
        void method1();
    }
    
    实现代码
    // A.cpp
    struct A::B {
        int i;
    }
    
    void A::method1() {
        //do something
    }
    
    void A::method2() {
        //do something
    }
    
    2012-01-12 20:57:03 回应
  • 第147页
    c++程序员在初始化时比c程序员要少考虑很多东西。
    在c程序中,操作对象是一个指针,而它指向的结构体在内存中还没有分配空间,需要手动p->initialize()一下。而在离开作用域时,还要手动p->clearup()一下,否则那块已经分配出去的内存因为指针变量出栈,再也找不回来了。
    而在c++程序中,当我们定义的同时就自动触发了类的构造函数完成初始化,在离开类变量作用域时,编译器又自动帮我们调用类的析构函数完成清除工作。
    c++将初始化和销毁视为了类定义不可分割的一部分。
    2012-01-12 21:32:53 回应
  • 第245页
    引用是一种“特殊”的指针,编译器强迫引用在被创建的时候,必须初始化,和某个存储单元进行关联。
    指针可以是空指针、可以指来指去,而引用不能是空引用,不能一会引用这个单元,一会儿引用另一个单元。
    它相当于一个alias,alias为空是没有任何意义的。
    引用最大的好处是,作为函数参数/返回值时,比指针更清晰(省得解引用了)。
    
    int* fun1(int* x) {
        (*x)++;
        return x; //safe
    }
    
    int& fun2(int& x) {   //相当于把指针变量x抽象成了一个alias,省得解引用了
        x++;         
        return x;  //safe
    }
    
    2012-01-27 21:09:11 回应
  • 第247页
    C语言按值传递
    int f(int x, char c);
    int g = f(a, b);
    
    push b ; (全局变量或者局部变量不重要)a和b的value 入栈
    push a
    call f() ; 进入f的代码段范围,此时f操作的是栈上的value,返回值搞在寄存器中
    add sp, 4 ; 弹栈栈上的value作废
    mov g, register ; 返回值保存在g里
    C之所以可以按值传递是因为所有value的大小都是编译器知道的(int,char……)
    2012-01-27 21:47:11 回应
  • 第310页
    为什么不能 delete void* ?
    delete需要知道指针的类型,在释放内存之前,调用改类型的析构函数(这也是c++引进delete的根本原因)
    该析构函数中可能又有对其它资源的 delete 操作,如果不调用就会泄露内存。
    而在 C 中,所有结构体的 initialize 和 destroy 都需要程序员自己调用,增加了出错的可能性。
    2012-02-05 16:52:41 回应
  • 第392页
    代码复用技术比较
    1.c方法:
    拷贝原来的代码,然后进行修改 = =
    2.smalltalk方法:
    继承类型树上和要实现的那个类最近的那个类,而不能随意创建
    3.c++方法
    模板,由编译完成参数代换
    2012-02-08 21:03:24 回应
  • 第364页
    class Instrument {
    public:
         virtual void play() {...}
    }
    
    class Violet : public Instrument {
    public:
        void play() {...}
    }
    
    void player(Instrument& i) {
        i.play();
    }
    
    int main() {
        Violet v;
        tune(v);
    }
    
    有了多态,player 可以不必考虑 i 是哪个 instrument,直接调用 i.play() ,i 就会发出它应该发出的声音。但问题时怎么找到 play 的代码段呢?
    如果基类方法不是 virtual, 运行时代码已经确定了,运行到 i.play(),就会自动跳到基类 play 代码段入口。(early binding)
    但如果发现基类方法是 virtual,程序还不知道接下去到哪执行?这时会触发编译器进行 late binding 来生成对应的代码入口。
    具体方法就是根据对象 v 中保存的 VPTR 指针找到 VTABLE 这张表,入口位于VTABLE[offset]。
    VPTR 和 VTABLE 在对象 violet 实例化的时候已经生成好(violet 已经知道自己是个小提琴= =),直到基类 virtual 函数被调用时才触发索引过程。
    因为虽然 violet 知道自己是个小提琴,但是 player 的代码段不能在编译时确定,如果舍弃多态,那么在编译时就可以绑定,即基类 play 函数代码。
    现在需要在运行时根据传入的对象来确定 player 到底该生成什么样的代码,我到底是拉小提琴还是弹吉他。
    late bingding 比 early binding 多了一个索引的过程,这个过程因为需要编译器的介入,减慢了调用的速度。
    开发效率和程序效率不可兼得,barne 选择了前者,于是乎 c++ 有了 virtual 关键字。
    2012-02-08 23:44:07 回应

[已注销]的其他笔记  · · · · · ·  ( 全部295条 )

The Design of Everyday Things
1
About Face 3
6
Engineering a Compiler
1
人有人的用处
8
Understanding the Linux Kernel
4
计算机程序设计艺术
1
公正
1
The Art of Doing Science and Engineering: Learning to Learn
1
科学革命的结构
7
罗素论教育
3
三十六大
1
娱乐至死
3
Real World Haskell
2
Writing Analytically
1
Is Parallel Programming Hard, And, If So, What Can You Do About It?
1
计算机与人脑
1
组合数学
2
菊与刀
1
Rework
5
翻译新究
4
计算机程序的构造和解释(原书第2版)
5
The Laws of Simplicity
4
计算机组成与设计硬件/软件接口
6
写给无神论者
2
放任自流的时光
3
哥德尔、艾舍尔、巴赫
2
树上的男爵
2
C++语言的设计与演化
1
Land of LISP
7
C陷阱与缺陷
2
CUDA by Example
3
C++沉思录
1
世界尽头与冷酷仙境
4
Head First C
2
刀锋
1
并行编程模式
2
The Ph.D. Grind
2
计算机系统结构
2
禅与摩托车维修艺术
14
流浪的面包树
2
翻译研究
18
An Introduction to Programming in Emacs Lisp
1
GNU Emacs Lisp 编程入门
1
计算机系统概论
1
编码
3
拖延心理学
1
古今数学思想(一)
1
挪威的森林
9
奇特的一生
7
GPU高性能运算之CUDA
5
那些年,我们一起追的女孩
8
十八岁给我一个姑娘
2
多核计算与程序设计
8
少有人走的路
5
忧伤的情欲
3
Hackers & Painters
7
哲学的慰藉
9
男人来自火星 女人来自金星
8
旅行的艺术
14
活着活着就老了
1
如何阅读一本书
7
Data-intensive Text Processing With Mapreduce
1
学习GNU Emacs
1
给研究生的学术建议
3
C专家编程
2
Spring揭秘
2
Head First Java(第二版·中文版)
1
自私的基因
1
C程序设计语言
2
计算机网络
3
自由在高处
2
大话设计模式
16
计算机网络
12