高程学习笔记

概述

高级程序设计复习笔记

参考书目:
《程序设计教程—用C++语言描述》陈家骏、郑滔
《Thinking in C++》机械工业出版社

第1章 复习程设基础

杂记

分数组成:
作业10%
上机40%(现场oj和课后projects)
期末50%(据说较为简单)

C/C++数据类型

unsigned类型运算不封闭,+*/封闭,-不封闭

float类型 0.1+0.2!=0.3之类的

实数相等条件,比较差的绝对值是否小于一个很小的数

联合类型用于用一种类型表示多种类型的数据,例如不同类型的数据组织在一起不能用array

指针是内存地址的抽象,用于动态变量(链表)、传递参数

C++过程抽象

C++函数数学函数区别:

  1. C++函数可以没有返回值
  2. 可以有副作用(side effect)
  3. 同一个实参,不同时刻调用结果可以不一样

内存区安排

静态数据区

  • 全局变量
  • static局部变量
  • 常量

第2章 对象与类

把成员函数定义放在声明处,是建议编译器按内联函数处理。

构造函数

创建动态对象不用 malloc 和 free,原因是不会调用构造函数和析构函数。

无参数或全是默认参数的构造函数,是默认构造函数

隐式的构造函数仅调用成员对象所在类和基类的构造函数

一旦随便自定义了构造函数,编译器不再提供隐式构造函数

隐式的析构函数仅调用成员对象所在类和基类的析构函数

复制字符串时候 new char(strlen(str) + 1)

数据成员初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class A {
int a;
const int b;
int& c;
public:
A(){a = 1;}
A(int m, int n):b(m), c(n){}
};

class B {
A a;
A1 a1;
A2 a2;
A3 a3;
int x;
public:
B(){x = 1;}
B(int m):a(m, m){x = 7;}
~B();
}

const int 和 int& 数据成员必须用成员初始化表,如果这样的类不自定义构造函数,编译器不会为其生成默认构造函数,因此这样的类不能用于构造对象?

成员对象的初始化:默认按照成员对象的默认构造函数来,除非用成员初始化表进行显式调用,顺序 A1-A2-A3-B函数体,析构顺序 B函数体-A3-A2-A1

拷贝构造函数

1
2
3
4
5
6
7
8
9
10
11
12
A(const A& a);
A(const A& a, int m = 1, int n = 2); // 后面的必须是默认参数
A(const A a); // 不行,考虑下面的例子

void f(A x);

int main(){
A re;
f(re);
// 调用时,x需要用re初始化,相当于A x=A(re)
// re又作为参数去初始化const A a,相当于A a = A(re)...无限递归[UST]
}

隐式拷贝构造函数:普通成员直接拷贝(涉及资源申请的小心),成员对象调用它的拷贝构造函数

自定义拷贝构造函数:默认按照成员对象的默认构造函数来,除非用成员初始化表显式调用成员对象的拷贝构造函数,如

1
2
3
4
5
6
7
8
9
class A {
...
A(const A& a);
}

class B {
A a;
B(const B& b):a(b.a) {...}
}

const 成员函数

const 对象不能调用非 const 成员函数,所以

1
2
f(const A* pa); // *pa无法被改变
f(const A& a); // a无法被改变

const 成员函数可以重载,效果[UST]

1
2
void f() const; // 谁调用?
void f(); // A a 和 const A a?

题外话

1
2
3
4
const int* p / int const * p; // *p无法修改
int* const p // p无法修改
const int* const p // *p和p均无法修改
记忆方式:const的右边第一个是*还是p

static 成员

算是 class 的属性,所有对象共享。

1
2
3
4
5
6
7
8
9
class A {
static int x = 0; // 或者类外 x = 1 都行
static B b; // 类外定义
}

A::x = 1; // ok
int main() {
A::x = 1; // error,别的函数看不到这一句
}

友元

访问 private 和 protected

数据保护数据访问效率的折衷

Demeter 法则

一个类的成员函数除了自己的直接子结构(数据成员),不能依赖于其他类

1
2
3
4
5
6
7
class A{
...
}

class B {
A func(); // 不遵守 Demeter 法则?
}

转移构造函数

系统自动调用,用于返回一个对象的时候,节约资源

1
2
3
4
A(A&& x) {
p = x.p;
x = NULL;
}

操作符重载

作为类的非静态成员函数

单目还是单目,双目还是双目。

*既作为解引用,又作为乘法,咋办?[UST][CJJ: *默认已经作为解引用重载好了,只管乘法就行]

“.”, “.*”, “::”, “?:”, “sizeof” 不能重载

A& operator++(); // 效果++a
A operator++(int i); // 效果a++,似乎是个右值表达式,似乎有骚操作

= 操作符重载

=操作符被隐式重载,但如果类中有 const int 和 int& 成员时,=不会隐式重载。

同拷贝构造函数的理,=操作符同样有即将消亡的对象赋值浪费的问题。
改进方法如下,该调用的时候编译器自动调用

1
2
3
4
5
6
7
8
A& operator=(A&& x) {
if(&x = *this){ // 防止递归
return *this;
}
delete[] x.p; // 释放原来的资源
p = x.p; // 转移资源
x.p = NULL;
}

new 重载

作为类的 static 函数重载,static 可以不写

1
2
void* operator new(size_t size) // size_t size必须有,别的无所谓
void* operator new(size_t size, int x)

#第3章 lamda 表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{ int k,m,n; //环境变量
......
...[](int x)->int { return x*x; }... //不能使用k、m、n
...[&](int x)->int { k++; m++; n++;
return x+k+m+n; }... //k、m、n可以被修改
...[=](int x)->int { return x+k+m+n; }...
//k、m、n不能被修改
...[&,n](int x)->int { k++; m++;
return x+k+m+n; }... //n不能被修改
...[=,&n](int x)->int { n++; return x+k+m+n; }...
//n可以被修改
...[&k,m](int x)->int { k++; return x+k+m; }...
//只能使用k和m,k可以被修改
...[=] { return k+m+n; }... //没有参数,返回值类型为int

}