5. 更好的 C
1.
指针常量
如何读指针定义:从标识符开始,由里向外读
const int* x;
int const * x;
x 是一个指针,它指向一个 const int
x 是一个指向恰好是 const 的 int 的普通指针
int * const x=&d;
x 是一个指针,这个指针是指向 int 的 const 指针
可以利用 *x=2 改变指向单元的值,但你不能 x=&c 改变 x 的值
const int* const x=&d;
int const * const x2=&d;
指针、指针指向的对象都不能变
意义:赋值时的类型检查
5
6. 更好的 C
1.
函数参数和返回值中的常量
void f1(const int i) {
i++; //illegal
}
变量 i 的初值不会被函数 f1 改变(在 f1 的活动期该值不变)
const int f2()
const 的返回值,不会对内部数据产生影响
对于用户定义数据类型,如类,该函数的返回值不能是左值(不能被赋值,也
不能被修改)
临时变量
6
7. 更好的 C
1.
变量块内定义
for (int ii=0;ii<100;ii++ )
{
int dwPos;
……
{
int dwPos;
……
}
……
}
变量块内变量的作用域和生存期
7
8. 更好的 C
1.
引用
int x;
Int &a=x;
a++;
const int &pi=1024;
a=pi;
引用,又称别名,是一个自动能被编译器逆向引用的常量型指针
使用引用的规则
•引用被创建时,必须被初始化(指针则可以在任何时候被初始化);
•引用被初始化为指向一个对象,它就不能被改变为对另一个对象的引用(指针则可以在任何时候指
向另一个对象);
•不存在 NULL 引用。必须确保引用是和一块合法的存储单元关联。
const 引用可以用不同类型的对象初始化,也可以是不可寻址的值,如文字常量。
引用和指针的区别
•引用必须总是指向一个对象
•引用间的赋值,改变了被引用的对象而不是引用本身
8
14. 更好的 C
1.
函数重载
int max (int, int)
int max (const vector<int> & );
int max (const matrix & );
函数重载( function overloading )允许多个函数共享同一个函数名,但是针对不同参数类
型提供共同的操作。
int max (int, int)
int max (int a, int b );
函数重复声明
int max (int, int)
double max (int a, int b );
错误函数重复声明
如果函数的功能不一样,请不要利用重载
14
31. 对象模型
1.
C++ 在布局以及存取时间上的主要额外负担是由 virtual 引
起的
C++ 的对象数据类型有 static , nonstatic
C++ 的对象方法有 static , nonstatic 和 virtual
31
32. 对象模型
1.
对象模型(不带多重继承)
class Point {
public:
Point (float xval);
virtual ~Point();
float x() const; float _x Type_info for Point
_vptr__Point
static int PointCount();
Point::~Point()
protected: Point::print(ostream&)
Virtual table
virtual ostream& print (ostream&os) const; for Point
Point::Point(float)
float _x; static int
static int
static int _point_count; Point::
Point::PointCount()
_point_count float Point::x()
}
32
33. 对象模型
1.
对象模型(多重继承)
class iostream: public istream, public ostream {… …};
class istream: virtual public ios {… …};
class ostream: virtual public ios {… …};
istream class
object
iostream class
object bptr
ios class
base class
bptr object
table for
ostream class
istream
object
base class
table for
bptr
iostream
base class
table for
ostream
33
38. 构造函数的语义
1.
缺省构造函数
对于 class X ,如果没有任何用户定义的构造函数,那么就会有一个缺省构造函数被声明出
来,这个缺省构造函数是一个 trivial 的构造函数
在以下情况下,编译器将为用户产生 nontrivial 的构造函数
•带有 Default Constructor 的 Member Class Object
•带有 Default Constructor 的 Base Class
•带有一个 Virtual Function 的 Class
•带有一个 Virtual Base Function 的 Class
38
42. 构造函数的语义
1.
类带有一个虚基类 class X {public: int I;};
不同的编译器有不同的实现方法
class A : public virtual X
编译器将改写 foo 并为 A , B , C 产生缺省构造函数
{public: int j; };
void foo (const A* pa) { pa->vbcX->I = 1024; }
// 编译器需要在 A , B , C 构造函数中对 vbcX 进行赋值 class B : public virtual X
,
{public: double d; };
// 需要生成缺省构造函数
class C: public A, public B
{public: int k; };
void foo (const A* pa) {
pa->i=1-24;
};
foo (new A);
foo (new B);
42
43. 构造函数的语义
1.
关于缺省构造函数的两个误解
如果类没有定义缺省构造函数,编译器将会合成出来一个
编译器合成的缺省构造函数会为类的每一个成员变量设定默认值
#include <stdio.h> int main () {
class A test(12);
{ test(32);
public: test(112);
int i return 0;
}; }
void test (int newVal) {
A a;
printf(“a.i=%dn”; a.i);
a.i=newVal;
}
43
44. 构造函数的语义
1.
拷贝构造函数的构建工作—何时使用拷贝构造函数
对一个 object 进行初始化工作
当 object 被作为参数传给某一个函数
函数传回一个 object
class X {… …}; X foo_bar()
X x; {
X xx=x; X xx;
……
return xx
extern void foo (X x);
}
void bar()
{
X xx;
…
foo(xx);
}
44
52. 构造函数的语义
1.
成员的初始化表顺序
按成员的声明顺序进行
class X { class X {
int i; int i;
int j; int j;
public: public:
X( int val ) : j( val), i(j) X( int val ) : j( val)
{} { i=j; }
} }
52
53. Data 语义学
1.
如何解释下面的结果
#include <stdio.h>
class X {};
class Y : public virtual X {} ;
class Z : public virtual X {} ;
class A : public Y, public Z {} ;
size X of is 1
size Y of is 4
int main () {
size Z of is 4
printf(quot;size X of is %dnquot;,sizeof(X));
size A of is 8
printf(quot;size Y of is %dnquot;,sizeof(Y));
printf(quot;size Z of is %dnquot;,sizeof(Z));
size X of is 1
printf(quot;size A of is %dnquot;,sizeof(A));
size Y of is 8
}
size Z of is 8
size A of is 12
53
54. Data 语义学
1.
对象的大小
对象的 nonstatic 数据成员的总和大小
Aligment 填充的空间
virtual 产生的负担
size X of is 1
size Y of is 8
size Z of is 8
size A of is 12
X 是 1 个字节,是为了每个对象有不同的地址( this 会指向不同的地方)
Y 、 Z 是 8 个字节,首先是 vptr ,然后是 1 个字节,和这个字节的填充
A 是 12 个字节,首先是 2 个 vptr ,然后是 1 个字节,和这个字节的填充
size X of is 1
size Y of is 4
size Z of is 4
size A of is 8
X 是 1 个字节,是为了每个对象有不同的地址( this 会指向不同的地方)
Y 、 Z 是 4 个字节,是 vptr ,这个 vptr 能够区分不同的对象,就不再引入一个字节
A 是 12 个字节,首先是 2 个 vptr ,这两个 vptr 能够区分不同的对象,就不再引入一个字节
54
55. Data 语义学
1.
数据成员的布局
静态成员不会影响数据布局
C++ 规范规定,同一个 access 段中,较晚出现的成员在对象中有较高的地址(边界调整)
C++ 规范允许不同 access 段的数据成员可以自由排列
class Point3d {
private:
float x;
class Point3d {
static List <point3d*> *freeList;
private:
float y;
float x;
static const int chunkSize=250;
static List <point3d*> *freeList;
float z;
private:
};
float y;
static const int chunkSize=250;
private:
float z;
};
55
78. Function 语义学
1.
inline 函数
inline 函数何时展开
1 :分析函数定义,决定是否可以进行展开
2 :在展开点上决定能否展开,需要引入参数求值和临时对象管理
对形式参数的管理
inline int min (int I, int j) {
return i<j ? I : j ;
}
minval= val1< val2 ? val1 : val2 ;
int minval;
int val1=1024; int val2=2048;
minval=1024;
minval=min ( val1, val2 );
int t1;
int t2;
minval=min (1024, 2048);
minval= (t1=foo()), (t2 = bar() +1 ),
t1<t2 ? t1: t2;
minval=min ( foo(), bar() + 1);
78
79. Function 语义学
1.
inline 函数
局部变量管理
inline int min (int I, int j) {
int minval= i<j ? I : j ;
return minval;
int __min_lv_minval;
}
minval=
(__min_lv_minval =
int minval;
val1<val2 ? val1 : val2 ),
int val1=1024; int val2=2048;
__min_lv_minval
minval=min ( val1, val2 );
79
80. 构造、析构和拷贝的语义
1.
无继承情况下对象的构造
由于 Point 的构造、析构函数都是 trivial , global 在程序的开始和结束都不会调用相应的构
造、析构函数
同理, local 的缺省构造、析构函数都没有被调用
typedef struct {
关于 heap 的 new 、 delete 操作,会展开
float x,y,z;
*heap=local 会出现一个警告 } Point;
warning, line 7: local is used before being initialized.
Point global;
Point foobar ()
{
Point local;
Point * heap= new Point;
Point *heap=__new ( sizeof ( Point) );
__delete ( heap ); *heap=local;
delete heap;
return local;
}
80
81. 构造、析构和拷贝的语义
1.
无继承情况下对象的构造(带构造函数)
global 在程序的开始时( startup )会调用相应的构造
local 的构造函数被调用,同时,它是个 inline 函数,会被展开
关于 heap 的 new 、 delete 操作,会展开
class Point {
public:
Point(float x=0.0 … ) : _x(x), …;
}
Point global;
Point foobar ()
Point local;
{
local._x = 0.0 ; … …
Point local;
Point * heap= new Point;
*heap=local; Point *heap = __ new ( sizeof ( Point ) ) ;
delete heap; if ( heap ! = 0 )
return local; heap -> Point:: Point ();
}
81
82. 构造、析构和拷贝的语义
1.
带虚函数情况下对象的构造 Point*
Point::Point ( Point* this, float x, float y )
构造函数会被附加一些代码,用于初始化 vptr
: _x(x), _y(y) {
合成一个拷贝构造函数和一个赋值演算符
展开 this->__vptr_Point = __vtbl_Point;
this->_x=x; this->_y=y;
class Point {
return this;
public:
}
Point(float x=0.0 … ) : _x(x), …{} ;
inline Point*
virtual float z ();
Point::Point ( Point* this, const Point& rhs) {
protected:
this->__vptr_Point = __vtbl_Point;
float _x, _y;
//memcpy 拷贝成员
}
return this;
Point global;
local 的构造和 new
)
Point foobar () 操作以后的初始化没
{ 变
Point local;
Point * heap= new Point;
heap->Point::Point (heap, local) ;
*heap=local;
delete heap;
return local;
}
82
85. C++ 内存管理
1.
m
m m m m
活动树
q(1,9)
r q(1,9) q(1,9)
名字和数据对象之间的绑定 q(1,3)
q(1,3)
活动树和栈
q(2,3)
int a[1024]; p(2,3)
dwMix=4
int readArray() { }
int partition(int begin,int end) { }
void quicksort(int begin,int end) { m
int dwMid=partition(begin,end);
quicksort(begin,dwMid-1); r q(1,9)
quicksort(dwMid+1,end);
p(1,9) q(1,3) q(5,9)
}
p(1,3) q(1,0) q(2,3) p(5,9) q(5,5) q(7,9)
int main() {
int dwEnd=readArray();
p(2,3) q(2,2) q(3,3) p(7,9) q(7,7) q(9,9)
quicksort(0,dwEnd);
}
dwMix=1 dwMix=8
85