记录侯捷老师C++面向对象课程的笔记。
- 当以类(class)来组织编写C++,可以把C++编写方式分为:Object Oriented(面向对象)和Object Based(基于对象),其区别主要看类与类之间是否有关联(继承,复合,委托)。
- 单一类有包含指针的类和不包含指针的类,以此为区分的类有很大的不同。
- C++由C++语言和C++标准库组成。
- C++经典书籍推荐:《C++ Primer》、《The C++ programming language(C++11)》、《Effective C++》、《The standard library》、《STL 源码剖析》。
- C语言与C++的明显的不同在于,在C语言中,数据变量是暴露在所有处理函数面前的,而C++中,特定的数据由特定的函数体处理(由class,struct包装起来的变量和函数)。
- 在C++中引用C语言的库,#include<stdio.h>或者 #include<cstdio>都可以。
- 头文件的防卫式声明
1
2
3
4
|
#ifndef __COMPLEX__
#define __COMPLEX__
...
#endif
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#ifndef __COMPLEX__
#define __COMPLEX__
template<typename T>
class complex
{
public:
complex (T r = 0, T i = 0)
:re(r), im(i){}
T real () const {return re;}
T imag () const {return im;}
private:
T re, im;
friend complex& __doapl__ (complex*, const complex&);
}
#endif
|
模板类的实例化:
1
2
|
complex<double> c1(2.5, 1.5);
complex<int> c2(2,6);
|
在这里,使用模板template的好处在于:不需要为不同类型double、int等单独定义不同的复数类,而这些类的区别仅仅在于数据类型不同而已。
- inline(内联)函数:若函数在class内定义完成,则成为inline函数,inline函数相较于一般函数,编译速度更快,但如果函数体过于复杂,编译器实际上不会把它当作inline函数。简洁来说,我们在函数前面加inline关键字,只不过是对编译器的建议而已,该函数最终不一定是inline函数。
- 大部分的函数访问级别标记为public,而所有的类的数据的访问级别标记为private。
- 类的构造函数写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class complex
{
public:
//构造函数的名称与类的名称相同,没有返回类型
complex (double r = 0, double i = 0) //默认实参
:re(r), im(i) //initialization list(初值列表):如果构造时引入参数,则使用该参数为类的变量赋值
{} //构造函数的函数体
complex& operator += (const complex&);
double real () const {return re;}
double imag () const {return im;}
private:
double re, im;
friend complex& __doapl__ (complex*, const complex&);
}
|
使用initialization list(初值列表)为类内变量赋值效率会更高,因为为一个变量赋值分为初始化和赋值(即{}内完成的部分)两个阶段,initialization list会在初始化阶段直接为变量赋值,省了一步。
- 不带指针的类多半不需要写析构函数。
- 构造函数可以有很多个--overloading(C++允许函数重载,不仅仅限于构造函数)
但需要注意下面的例子,这两个构造函数却不可以共存。
1
2
3
4
5
|
complex(double r = 0, double i = 0) //该构造函数有默认参数
:re(r), im(i) {}
complex() : re(0), im(0) {} //上面的构造函数有默认参数,导致该构造函数不能成功构建,
//因为对于一个例子:conplex C;该实例化会让编译器无法决定使用哪一个,
//因为上面两个构造函数都是相同的效果
|
- 构造函数放在private作用域:不允许随意外界创建该类的实例。(singleton)
- const:当确保函数不改变数据的内容,请加const。
1
2
|
double real () const {return re;}
double imag () const {return im;}
|
- 尽量使用引用传递参数(pass by reference)。
- 常量引用:const conplex&, 引用保证参数传递很快,常量const保证参数不可修改。
- 尽量使用引用返回函数值(return by reference)。
- 相同class的各个objects互为友元(friends)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class complex
{
public:
//构造函数的名称与类的名称相同,没有返回类型
complex (double r = 0, double i = 0) //默认实参
:re(r), im(i)
{}
int func(const complex& param){
return param.re + param.im;
}
private:
double re, im;
}
|
- 当返回的变量来自于一个局部变量的赋值,这种情况不可以返回引用,因为局部变量超出函数作用域后就会消亡。
- 可以认为操作符就是函数
- 操作符重载--写成成员函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# 传递者无需知道接收者是否以 reference形式接收参数(reference的另外一个好处)
inline complex&
__doapl(complex* ths, const complex& r)
{
ths->re += r.re;
ths->im += r.im;
return *ths;
}
#所有的成员函数都有一个隐藏的this
#之所以这里不能是void 是考虑到:a+=b+=c 这种用法
inline complex&
complex::operator += (const complex& r)
{
return __doapl(this, r); # this在这里显式调用
}
|
- 操作符重载--写成非成员函数,它是全局函数,没有this指针,必须显示调用对象的实例
- 临时对象的返回不可以返回引用
1
2
3
4
5
|
inline complex
conj (const complex& x)
{
return complex (real(x), -imag(x));
}
|
1
2
3
4
5
6
7
|
#include <iostream.h>
# 返回类型不是void 是考虑到 cout<<c1<<c2;的形式
ostream&
operator << (ostream& os, const complex& x)
{
return os << '(' << real(x)<<','<<imag(x)<<')';
}
|