C++(四) Effective-C++(下)
原图
模板与泛型编程
条款 41:了解隐式接口和编译期多态
面向对象编程世界总是以显式接口( explicit interfaces)和运行期多态( runtime polymorphism)解决问题。Templates 及泛型编程的世界,与面向对象有根本上的不同。在此世界中显式接口和运行期多态仍然存在,但重要性降低。反倒是隐式接口( implicit interfaces)和编译期多态( compile-time polymorphism)移到前头了。
123456789template<typename T>void doprocessing(T& w){ if (w.size() > 10 && w != somenastywidget) { T temp (w); temp.normalize (); temp.swap(w); }}
w 必须支持哪一种接口,系由 template 中执行于 w 身上的操作来决定。本例看来 w 的类型好像必须支持 size, normali ...
C++(三) Effective C++(中)
原图
设计与声明
条款 19:设计 class 犹如设计 type
C++就像在其他 OOP(面向对象编程)语言一样,当你定义一个新 class,也就定义了一个新 type。这意味你并不只是 class 设计者,还是 type 设计者。重载( overloading)函数和操作符、控制内存的分配和归还、定义对象的初始化和终结全都在你手上。因此你应该带着和“语言设计者当初设计语言内置类型时”一样的谨慎来研讨 class 的设计。
条款 20:宁以传递 const 引用替换传递值
缺省情况下 C++以 by value 方式传递对象至(或来自)函数。除非你另外指定,否则函数参数都是以实际实参的复件(副本)为初值,而调用端所获得的亦是函数返回值的一个复件。这些复件(副本)系由对象的 capy 构造函数产出,这可能使得 pass-by-value 成为费时的操作。
如果有什么方法可以回避所有那些构造和析构动作就太好了。有的,就是 pass by reference-to-const:这种传递方式的效率高得多:没有任何构造函数或析构函数被调用,因为没有任何新对象被创建。修订后的这个参数 ...
C++(二) Effective C++(上)
原图
让自己习惯 C++
条款 03:尽可能使用 const
如果关键字 const 出现在星号左边,表示被指物是常量;如果出现在星号右边,表示指针自身是常量;如果出现在星号两边,表示被指物和指针两者都是常量。
12345char greeting[] = "Hello";char* p = greeting; //non-const pointer,non-const dataconst char* p greeting; //non-const pointer,const datachar* const p = greeting; //const pointer,non-const dataconst char* const p = greeting; //const pointer,const data
条款 04:确定对象被使用前已先被初始化
为内置型对象进行手工初始化,因为 C++不保证初始化它们。
构造函数最好使用成员初值列(member initialization list),而不要在构造函数本体内使用赋值操作(assignment)。初值列列出的成员变 ...
C++(一)对象模型
原图
对象模式
在 C++中,有两种 class data members:static 和 nonstatic,以及三种 class member functions:static、nonstatic 和 virtual。已知下面这个 class Point 声明:
123456789101112class Point { public: Point(float xval); virtual ~Point(); float x() const static int PointCount(); protected: virtual ostream& print(ostream &os) const; float _x; static int _point_count;};
这个 class Point 在机器中将会被怎么样表现呢?也就是说,我们如何模塑(modeling)出各种 data members 和 function members 呢?
C对象模型(The C Object Model)
Stro ...
链接装载与库(五)动态链接
原图
为什么要动态链接
内存和磁盘空间:
/usr/bin 下就有数千个可执行文件,还有其他数以千计的库如果都需要静态链接,那么空间浪费无法想象
程序开发和发布:
一旦程序中有任何模块更新,整个程序就要重新链接、发布给用户
程序可扩展性和兼容性:
动态链接还可以加强程序的兼容性。一个程序在不同的平台运行时可以动态地链接到由操作系统提供的动态链接库
存在的问题:
当程序所依赖的某个模块更新后,由于新的模块与旧的模块之间接口不兼容,导致了原有的程序无法运行
简单的动态链接例子
我们分别需要如下几个源文件:“Program1.c”、“Program2.c”、“Lib.c”和“Lib.h”。
12345678910111213141516171819202122232425262728293031323334清单 7-1 SimpleDynamicalLinking/*Program1.c*/#include "Lib.h"int main(){ foobar(1); return 0;}/*Program2.c */#include "Lib.h"in ...
链接装载与库(四)可执行文件的装载与进程
原图
装载的方式
程序执行时所需要的指令和数据必须在内存中才能够正常运行,最简单的办法就是将程序运行所需要的指令和数据全都装入内存中,这就是最简单的静态装入的办法。后来研究发现,程序运行时是有局部性原理的,所以我们可以将程序最常用的部分驻留在内存中,而将一些不太常用的数据存放在磁盘里面,这就是动态装入的基本原理。
覆盖装入(Overlay)和页映射(Paging)是两种很典型的动态装载方法,它们所采用的思想都差不多,原则上都是利用了程序的局部性原理。动态装入的思想是程序用到哪个模块,就将哪个模块装入内存,如果不用就暂时不装入,存放在磁盘中。
页映射
将内存和所有磁盘中的数据和指令按照“页(Page)”为单位划分成若干个页,以后所有的装载和操作的单位就是页。
为了演示页映射的基本机制,假设我们的 32 位机器有 16KB 的内存,每个页大小为 4096 字节,则共有 4 个页,如下表所示。
页编号
地址
F0
0x00000000 - 0xFFFFF000
F1
0x00001000 - 0x00001FFF
F2
0X00002000 - 0X00002F ...
链接装载与库(三)静态链接
原图
在这一节里,我们将使用下面这两个源代码文件“a.c”和“b.c”作为例子展开分析:
假设我们的程序只有这两个模块“a.c”和“b.c”。首先我们使用 gcc 将“a.c”和“b.c”分别编译成目标文件“a.o”和“b.o”:
1gcc -c a.c b.c
从代码中可以看到,“b.c”总共定义了两个全局符号,一个是变量“shared”,另外一个是函数“swap”;“a.c”里面定义了一个全局符号就是“main”。模块“a.c”里面引用到了“b.c”里面的“swap”和“shared”。我们接下来要做的就是把“a.o”和“b.o”这两个目标文件链接在一起并最终形成一个可执行文件“ab”。
空间与地址分配
对于多个输入目标文件,链接器如何将它们的各个段合并到输出文件?
按序叠加
如图 4-1 所示,就是直接将各个目标文件依次合并,但是在有很多输入文件的情况下,输出文件将会有很多零散的段,这种做法非常浪费空间,因为每个段都须要有一定的地址和空间对齐要求。
相似段合并
一个更实际的方法是将相同性质的段合并到一起,比如将所有输入文件的“.text”合并到输出文件的“.text ...
链接装载与库(二)目标文件
原图
目标文件从结构上讲,它是已经编译后的可执行文件格式,只是还没有经过链接的过程,其中可能有些符号或有些地址还没有被调整。
目标文件的格式
现在 PC 平台流行的可执行文件格式主要是 Windows 下的 PE(Portable Executable)和 Linux 的 ELF(Executable Linkable Format),它们都是 COFF(Common fileformat)格式的变种。
ELF 文件标准里面把系统中采用 ELF 格式的文件归为如下表所列举的 4 类。
ELF 文件类型
说明
实例
可重定位文件 Relocatable File
这类文件包含了代码和数据,可以被用来链接成可执行文件或共享目标文件,静态链接库也可以归为这一类
Linux 的。o Windows 的。obj
可执行文件 Executable File
这类文件包含了可以直接执行的程序,它的代表就是 ELF 可执行文件,它们一般都没有扩展名
比如/bin/bash 文件 Windows 的。exe
共享目标文件 Shared Object File
这种文 ...
链接装载与库(一)编译和链接
原图
被隐藏了的过程
1234567#include <stdio.h>int main(){ printf("Hello World\n"); return 0;}
在 Linux 下,当我们使用 GCC 来编译 Hello World 程序时,只须使用最简单的命令(假设源代码文件名为 hello.c):
123gcc hello.c./a.outHello world
事实上,上述过程可以分解为 4 个步骤,分别是预处理(Prepressing)、编译(Compilation)、汇编(Assembly)和链接(Linking)。
预编译
第一步预编译的过程相当于如下命令(-E 表示只进行预编译):
1gcc -E hello.c -o hello.i
或者:
1cpp hello.c hello.i
预编译过程主要处理那些源代码文件中的以“#”开始的预编译指令。比如“#include”、“#define”等,预编译成一个。i 文件,主要处理规则如下:
将所有的“#define”删除,并且展开所有的宏定义
处理所有条件预编译指令,比如“#if”、“ ...
基于反作用轮的 3D 倒立摆的非线性分析与控制
摘要
本文介绍了基于反作用轮的 3D 倒立摆 Cubli 的非线性分析和控制设计。 使用广义动量的概念,将基于反作用轮的 3D 倒立摆的关键特性与 1D 情况的特性进行比较,以提出相对简单和直观的非线性控制器。 最后,在 Cubli 上实现了所提出的控制器,并给出了实验结果
INTRODUCTION
本文介绍了图 1 所示 Cubli 的非线性分析和控制。Cubli 是一个边长为 150 mm 的立方体,三个反作用轮相互垂直安装。 与其他 3D 倒立摆试验台 [1] 和 [2] 相比,Cubli 具有两个独特的功能。 一是其占地面积相对较小(因此得名 Cubli,源自瑞士德语中“立方体”的缩略词)。 另一个特点是它能够在没有任何外部支撑的情况下从静止位置跳起来,通过突然制动以高角速度旋转的反作用轮。 在 [3] 中介绍了跳跃的概念,在 [4] 中介绍了线性控制器,本文重点分析非线性模型和非线性控制策略。
与 [5] 和 [6] 中提出的工作相反,本文从纯机械的角度来处理控制器设计。 Cubli 的基本机械特性被利用来提出具有物理洞察力的简单直观的推导([5]:1D,反作用轮,反馈线 ...
